blob: 457826bfa90e920190f41dae9fca85095ccde4c2 [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
Tao Bao43078aa2015-04-21 14:32:35 -070040 --full_radio
41 When generating an incremental OTA, always include a full copy of
42 radio image. This option is only meaningful when -i is specified,
43 because a full radio is always included in a full OTA if applicable.
44
leozwangaa6c1a12015-08-14 10:57:58 -070045 --full_bootloader
46 Similar to --full_radio. When generating an incremental OTA, always
47 include a full copy of bootloader image.
48
Michael Runge63f01de2014-10-28 19:24:19 -070049 -v (--verify)
50 Remount and verify the checksums of the files written to the
51 system and vendor (if used) partitions. Incremental builds only.
52
Michael Runge6e836112014-04-15 17:40:21 -070053 -o (--oem_settings) <file>
54 Use the file to specify the expected OEM-specific properties
55 on the OEM partition of the intended device.
56
Tao Bao8608cde2016-02-25 19:49:55 -080057 --oem_no_mount
58 For devices with OEM-specific properties but without an OEM partition,
59 do not mount the OEM partition in the updater-script. This should be
60 very rarely used, since it's expected to have a dedicated OEM partition
61 for OEM-specific properties. Only meaningful when -o is specified.
62
Doug Zongkerdbfaae52009-04-21 17:12:54 -070063 -w (--wipe_user_data)
64 Generate an OTA package that will wipe the user data partition
65 when installed.
66
Tao Bao5d182562016-02-23 11:38:39 -080067 --downgrade
68 Intentionally generate an incremental OTA that updates from a newer
69 build to an older one (based on timestamp comparison). "post-timestamp"
70 will be replaced by "ota-downgrade=yes" in the metadata file. A data
71 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080072 the metadata file. The update-binary in the source build will be used in
73 the OTA package, unless --binary flag is specified.
Tao Bao5d182562016-02-23 11:38:39 -080074
Doug Zongker1c390a22009-05-14 19:06:36 -070075 -e (--extra_script) <file>
76 Insert the contents of file at the end of the update script.
77
Hristo Bojinovdafb0422010-08-26 14:35:16 -070078 -a (--aslr_mode) <on|off>
79 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050080
Doug Zongker9b23f2c2013-11-25 14:44:12 -080081 -2 (--two_step)
82 Generate a 'two-step' OTA package, where recovery is updated
83 first, so that any changes made to the system partition are done
84 using the new recovery (new kernel, etc.).
85
Doug Zongker26e66192014-02-20 13:22:07 -080086 --block
87 Generate a block-based OTA if possible. Will fall back to a
88 file-based OTA if the target_files is older and doesn't support
89 block-based OTAs.
90
Doug Zongker25568482014-03-03 10:21:27 -080091 -b (--binary) <file>
92 Use the given binary as the update-binary in the output package,
93 instead of the binary in the build's target_files. Use for
94 development only.
95
Martin Blumenstingl374e1142014-05-31 20:42:55 +020096 -t (--worker_threads) <int>
97 Specifies the number of worker-threads that will be used when
98 generating patches for incremental updates (defaults to 3).
99
Tao Bao8dcf7382015-05-21 14:09:49 -0700100 --stash_threshold <float>
101 Specifies the threshold that will be used to compute the maximum
102 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800103
104 --gen_verify
105 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800106
107 --log_diff <file>
108 Generate a log file that shows the differences in the source and target
109 builds for an incremental package. This option is only meaningful when
110 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700111
112 --payload_signer <signer>
113 Specify the signer when signing the payload and metadata for A/B OTAs.
114 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
115 with the package private key. If the private key cannot be accessed
116 directly, a payload signer that knows how to do that should be specified.
117 The signer will be supplied with "-inkey <path_to_key>",
118 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700119
120 --payload_signer_args <args>
121 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700122"""
123
124import sys
125
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800126if sys.hexversion < 0x02070000:
127 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -0700128 sys.exit(1)
129
Doug Zongkerfc44a512014-08-26 13:10:25 -0700130import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700131import os
Tao Baoc098e9e2016-01-07 13:03:56 -0800132import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700133import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700134import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700135import zipfile
136
137import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700138import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700139import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700140
141OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700142OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700143OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700144OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700145OPTIONS.require_verbatim = set()
146OPTIONS.prohibit_verbatim = set(("system/build.prop",))
147OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700148OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800149OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700150OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700151OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700152OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
153if OPTIONS.worker_threads == 0:
154 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800155OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900156OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800157OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800158OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700159OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800160OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700161OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700162OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700163OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700164# Stash size cannot exceed cache_size * threshold.
165OPTIONS.cache_size = None
166OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800167OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800168OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700169OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700170OPTIONS.payload_signer_args = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700171
Doug Zongkereef39442009-04-02 12:14:19 -0700172def MostPopularKey(d, default):
173 """Given a dict, return the key corresponding to the largest
174 value. Returns 'default' if the dict is empty."""
175 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700176 if not x:
177 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700178 x.sort()
179 return x[-1][1]
180
181
182def IsSymlink(info):
183 """Return true if the zipfile.ZipInfo object passed in represents a
184 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700185 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700186
Hristo Bojinov96be7202010-08-02 10:26:17 -0700187def IsRegular(info):
188 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700189 regular file."""
190 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700191
Michael Runge4038aa82013-12-13 18:06:28 -0800192def ClosestFileMatch(src, tgtfiles, existing):
193 """Returns the closest file match between a source file and list
194 of potential matches. The exact filename match is preferred,
195 then the sha1 is searched for, and finally a file with the same
196 basename is evaluated. Rename support in the updater-binary is
197 required for the latter checks to be used."""
198
199 result = tgtfiles.get("path:" + src.name)
200 if result is not None:
201 return result
202
203 if not OPTIONS.target_info_dict.get("update_rename_support", False):
204 return None
205
206 if src.size < 1000:
207 return None
208
209 result = tgtfiles.get("sha1:" + src.sha1)
210 if result is not None and existing.get(result.name) is None:
211 return result
212 result = tgtfiles.get("file:" + src.name.split("/")[-1])
213 if result is not None and existing.get(result.name) is None:
214 return result
215 return None
216
Dan Albert8b72aef2015-03-23 19:13:21 -0700217class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700218 def __init__(self, partition, fs_config):
219 self.partition = partition
220 self.fs_config = fs_config
221 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700222
Dan Albert8b72aef2015-03-23 19:13:21 -0700223 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700224 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700225 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700226 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700227
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700228 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700229 # The target_files contains a record of what the uid,
230 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700231 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700232
233 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700234 if not line:
235 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700236 columns = line.split()
237 name, uid, gid, mode = columns[:4]
238 selabel = None
239 capabilities = None
240
241 # After the first 4 columns, there are a series of key=value
242 # pairs. Extract out the fields we care about.
243 for element in columns[4:]:
244 key, value = element.split("=")
245 if key == "selabel":
246 selabel = value
247 if key == "capabilities":
248 capabilities = value
249
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700250 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700251 if i is not None:
252 i.uid = int(uid)
253 i.gid = int(gid)
254 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700255 i.selabel = selabel
256 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700257 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700258 i.children.sort(key=lambda i: i.name)
259
Tao Baof2cffbd2015-07-22 12:33:18 -0700260 # Set metadata for the files generated by this script. For full recovery
261 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700262 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700263 if i:
264 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700265 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700266 if i:
267 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700268
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700269
Dan Albert8b72aef2015-03-23 19:13:21 -0700270class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700271 """Items represent the metadata (user, group, mode) of files and
272 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700273 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700274 self.itemset = itemset
275 self.name = name
276 self.uid = None
277 self.gid = None
278 self.mode = None
279 self.selabel = None
280 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700281 self.is_dir = is_dir
282 self.descendants = None
283 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700284
285 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700286 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700287 self.parent.children.append(self)
288 else:
289 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700290 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700291 self.children = []
292
293 def Dump(self, indent=0):
294 if self.uid is not None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700295 print "%s%s %d %d %o" % (
296 " " * indent, self.name, self.uid, self.gid, self.mode)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700297 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700298 print "%s%s %s %s %s" % (
299 " " * indent, self.name, self.uid, self.gid, self.mode)
300 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700301 print "%s%s" % (" "*indent, self.descendants)
302 print "%s%s" % (" "*indent, self.best_subtree)
303 for i in self.children:
304 i.Dump(indent=indent+1)
305
Doug Zongkereef39442009-04-02 12:14:19 -0700306 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700307 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700308 all children and determine the best strategy for using set_perm_recursive
309 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700310 values. Recursively calls itself for all descendants.
311
Dan Albert8b72aef2015-03-23 19:13:21 -0700312 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
313 counting up all descendants of this node. (dmode or fmode may be None.)
314 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
315 fmode, selabel, capabilities) tuple that will match the most descendants of
316 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700317 """
318
Dan Albert8b72aef2015-03-23 19:13:21 -0700319 assert self.is_dir
320 key = (self.uid, self.gid, self.mode, None, self.selabel,
321 self.capabilities)
322 self.descendants = {key: 1}
323 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700324 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700325 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700326 for k, v in i.CountChildMetadata().iteritems():
327 d[k] = d.get(k, 0) + v
328 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700329 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700330 d[k] = d.get(k, 0) + 1
331
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700332 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
333 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700334
335 # First, find the (uid, gid) pair that matches the most
336 # descendants.
337 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700338 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700339 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
340 ug = MostPopularKey(ug, (0, 0))
341
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700342 # Now find the dmode, fmode, selabel, and capabilities that match
343 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700344 best_dmode = (0, 0o755)
345 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700346 best_selabel = (0, None)
347 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700348 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700349 if k[:2] != ug:
350 continue
351 if k[2] is not None and count >= best_dmode[0]:
352 best_dmode = (count, k[2])
353 if k[3] is not None and count >= best_fmode[0]:
354 best_fmode = (count, k[3])
355 if k[4] is not None and count >= best_selabel[0]:
356 best_selabel = (count, k[4])
357 if k[5] is not None and count >= best_capabilities[0]:
358 best_capabilities = (count, k[5])
359 self.best_subtree = ug + (
360 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700361
362 return d
363
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700364 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700365 """Append set_perm/set_perm_recursive commands to 'script' to
366 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700367 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700368
369 self.CountChildMetadata()
370
371 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700372 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
373 # that the current item (and all its children) have already been set to.
374 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700375 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700376 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700377 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700378 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700379 current = item.best_subtree
380
381 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700382 item.mode != current[2] or item.selabel != current[4] or \
383 item.capabilities != current[5]:
384 script.SetPermissions("/"+item.name, item.uid, item.gid,
385 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700386
387 for i in item.children:
388 recurse(i, current)
389 else:
390 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700391 item.mode != current[3] or item.selabel != current[4] or \
392 item.capabilities != current[5]:
393 script.SetPermissions("/"+item.name, item.uid, item.gid,
394 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700395
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700396 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700397
398
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700399def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
400 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700401 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800402 list of symlinks. output_zip may be None, in which case the copy is
403 skipped (but the other side effects still happen). substitute is an
404 optional dict of {output filename: contents} to be output instead of
405 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700406 """
407
408 symlinks = []
409
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700410 partition = itemset.partition
411
Doug Zongkereef39442009-04-02 12:14:19 -0700412 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700413 prefix = partition.upper() + "/"
414 if info.filename.startswith(prefix):
415 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700416 if IsSymlink(info):
417 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700418 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700419 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700420 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700421 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700422 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700423 if substitute and fn in substitute and substitute[fn] is None:
424 continue
425 if output_zip is not None:
426 if substitute and fn in substitute:
427 data = substitute[fn]
428 else:
429 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700430 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700431 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700432 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700433 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700434 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700435
436 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800437 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700438
439
Doug Zongkereef39442009-04-02 12:14:19 -0700440def SignOutput(temp_zip_name, output_zip_name):
441 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
442 pw = key_passwords[OPTIONS.package_key]
443
Doug Zongker951495f2009-08-14 12:44:19 -0700444 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
445 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700446
447
Dan Albert8b72aef2015-03-23 19:13:21 -0700448def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700449 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700450 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700451 device = GetBuildProp("ro.product.device", info_dict)
452 script.AssertDevice(device)
453 else:
454 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700455 raise common.ExternalError(
456 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700457 for prop in oem_props.split():
458 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700459 raise common.ExternalError(
460 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700461 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700462
Doug Zongkereef39442009-04-02 12:14:19 -0700463
Tao Baod42e97e2016-11-30 12:11:57 -0800464def _WriteRecoveryImageToBoot(script, output_zip):
465 """Find and write recovery image to /boot in two-step OTA.
466
467 In two-step OTAs, we write recovery image to /boot as the first step so that
468 we can reboot to there and install a new recovery image to /recovery.
469 A special "recovery-two-step.img" will be preferred, which encodes the correct
470 path of "/boot". Otherwise the device may show "device is corrupt" message
471 when booting into /boot.
472
473 Fall back to using the regular recovery.img if the two-step recovery image
474 doesn't exist. Note that rebuilding the special image at this point may be
475 infeasible, because we don't have the desired boot signer and keys when
476 calling ota_from_target_files.py.
477 """
478
479 recovery_two_step_img_name = "recovery-two-step.img"
480 recovery_two_step_img_path = os.path.join(
481 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
482 if os.path.exists(recovery_two_step_img_path):
483 recovery_two_step_img = common.GetBootableImage(
484 recovery_two_step_img_name, recovery_two_step_img_name,
485 OPTIONS.input_tmp, "RECOVERY")
486 common.ZipWriteStr(
487 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
488 print "two-step package: using %s in stage 1/3" % (
489 recovery_two_step_img_name,)
490 script.WriteRawImage("/boot", recovery_two_step_img_name)
491 else:
492 print "two-step package: using recovery.img in stage 1/3"
493 # The "recovery.img" entry has been written into package earlier.
494 script.WriteRawImage("/boot", "recovery.img")
495
496
Doug Zongkerc9253822014-02-04 12:17:58 -0800497def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700498 namelist = [name for name in target_files_zip.namelist()]
499 return ("SYSTEM/recovery-from-boot.p" in namelist or
500 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700501
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700502def HasVendorPartition(target_files_zip):
503 try:
504 target_files_zip.getinfo("VENDOR/")
505 return True
506 except KeyError:
507 return False
508
Michael Runge6e836112014-04-15 17:40:21 -0700509def GetOemProperty(name, oem_props, oem_dict, info_dict):
510 if oem_props is not None and name in oem_props:
511 return oem_dict[name]
512 return GetBuildProp(name, info_dict)
513
514
515def CalculateFingerprint(oem_props, oem_dict, info_dict):
516 if oem_props is None:
517 return GetBuildProp("ro.build.fingerprint", info_dict)
518 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700519 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
520 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
521 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
522 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700523
Doug Zongkerfc44a512014-08-26 13:10:25 -0700524
Doug Zongker3c84f562014-07-31 11:06:30 -0700525def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700526 # Return an image object (suitable for passing to BlockImageDiff)
527 # for the 'which' partition (most be "system" or "vendor"). If a
528 # prebuilt image and file map are found in tmpdir they are used,
529 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700530
531 assert which in ("system", "vendor")
532
533 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700534 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
535 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700536 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700537 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700538
539 else:
540 print "building %s.img from target-files" % (which,)
541
542 # This is an 'old' target-files, which does not contain images
543 # already built. Build them.
544
Doug Zongkerfc44a512014-08-26 13:10:25 -0700545 mappath = tempfile.mkstemp()[1]
546 OPTIONS.tempfiles.append(mappath)
547
Doug Zongker3c84f562014-07-31 11:06:30 -0700548 import add_img_to_target_files
549 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700550 path = add_img_to_target_files.BuildSystem(
551 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700552 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700553 path = add_img_to_target_files.BuildVendor(
554 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700555
Tao Baoff777812015-05-12 11:42:31 -0700556 # Bug: http://b/20939131
557 # In ext4 filesystems, block 0 might be changed even being mounted
558 # R/O. We add it to clobbered_blocks so that it will be written to the
559 # target unconditionally. Note that they are still part of care_map.
560 clobbered_blocks = "0"
561
562 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700563
564
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700565def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700566 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700567 # be installed on top of. For now, we expect the API just won't
568 # change very often. Similarly for fstab, it might have changed
569 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700570 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700571
Tao Bao838c68f2016-03-15 19:16:18 +0000572 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700573 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -0700574 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700575 if oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700576 if OPTIONS.oem_source is None:
577 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800578 if not OPTIONS.oem_no_mount:
579 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700580 oem_dict = common.LoadDictionaryFromLines(
581 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700582
Tao Bao3e30d972016-03-15 13:20:19 -0700583 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700584 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700585 "post-build": target_fp,
Dan Albert8b72aef2015-03-23 19:13:21 -0700586 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
587 OPTIONS.info_dict),
588 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
589 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700590
Doug Zongker05d3dea2009-06-22 11:32:31 -0700591 device_specific = common.DeviceSpecificParams(
592 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700593 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700594 output_zip=output_zip,
595 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700596 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700597 metadata=metadata,
598 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700599
Doug Zongkerc9253822014-02-04 12:17:58 -0800600 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800601 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800602
Tao Baod8d14be2016-02-04 14:26:02 -0800603 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
604
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700605 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
606 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
607 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700608
Michael Runge6e836112014-04-15 17:40:21 -0700609 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700610 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800611
612 # Two-step package strategy (in chronological order, which is *not*
613 # the order in which the generated script has things):
614 #
615 # if stage is not "2/3" or "3/3":
616 # write recovery image to boot partition
617 # set stage to "2/3"
618 # reboot to boot partition and restart recovery
619 # else if stage is "2/3":
620 # write recovery image to recovery partition
621 # set stage to "3/3"
622 # reboot to recovery partition and restart recovery
623 # else:
624 # (stage must be "3/3")
625 # set stage to ""
626 # do normal full package installation:
627 # wipe and install system, boot image, etc.
628 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700629 # complete script normally
630 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800631
632 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
633 OPTIONS.input_tmp, "RECOVERY")
634 if OPTIONS.two_step:
635 if not OPTIONS.info_dict.get("multistage_support", None):
636 assert False, "two-step packages not supported by this build"
637 fs = OPTIONS.info_dict["fstab"]["/misc"]
638 assert fs.fs_type.upper() == "EMMC", \
639 "two-step packages only supported on devices with EMMC /misc partitions"
640 bcb_dev = {"bcb_dev": fs.device}
641 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
642 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700643if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800644""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800645
646 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
647 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800648 script.WriteRawImage("/recovery", "recovery.img")
649 script.AppendExtra("""
650set_stage("%(bcb_dev)s", "3/3");
651reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700652else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800653""" % bcb_dev)
654
Tao Baod42e97e2016-11-30 12:11:57 -0800655 # Stage 3/3: Make changes.
656 script.Comment("Stage 3/3")
657
Tao Bao6c55a8a2015-04-08 15:30:27 -0700658 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700659 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700660
Doug Zongkere5ff5902012-01-17 10:55:37 -0800661 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700662
Doug Zongker01ce19c2014-02-04 13:48:15 -0800663 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700664
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700665 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800666 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700667 if HasVendorPartition(input_zip):
668 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700669
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400670 # Place a copy of file_contexts.bin into the OTA package which will be used
671 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700672 if "selinux_fc" in OPTIONS.info_dict:
673 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500674
Michael Runge7cd99ba2014-10-22 17:21:48 -0700675 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
676
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700677 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700678 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800679
Doug Zongker26e66192014-02-20 13:22:07 -0800680 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700681 # Full OTA is done as an "incremental" against an empty source
682 # image. This has the effect of writing new data from the package
683 # to the entire partition, but lets us reuse the updater code that
684 # writes incrementals to do it.
685 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
686 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700687 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700688 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800689 else:
690 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700691 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800692 if not has_recovery_patch:
693 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800694 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700695
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700696 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800697 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700698
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700699 boot_img = common.GetBootableImage(
700 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800701
Doug Zongker91a99c22014-05-09 13:15:01 -0700702 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800703 def output_sink(fn, data):
704 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700705 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800706
707 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
708 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700709
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700710 system_items.GetMetadata(input_zip)
711 system_items.Get("system").SetPermissions(script)
712
713 if HasVendorPartition(input_zip):
714 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
715 script.ShowProgress(0.1, 0)
716
717 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700718 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
719 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700720 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700721 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700722 else:
723 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700724 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700725 script.UnpackPackageDir("vendor", "/vendor")
726
727 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
728 script.MakeSymlinks(symlinks)
729
730 vendor_items.GetMetadata(input_zip)
731 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700732
Doug Zongker37974732010-09-16 17:44:38 -0700733 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700734 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700735
Doug Zongker01ce19c2014-02-04 13:48:15 -0800736 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700737 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700738
Doug Zongker01ce19c2014-02-04 13:48:15 -0800739 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700740 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700741
Doug Zongker1c390a22009-05-14 19:06:36 -0700742 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700743 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700744
Doug Zongker14833602010-02-02 13:12:04 -0800745 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800746
Doug Zongker922206e2014-03-04 13:16:24 -0800747 if OPTIONS.wipe_user_data:
748 script.ShowProgress(0.1, 10)
749 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700750
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800751 if OPTIONS.two_step:
752 script.AppendExtra("""
753set_stage("%(bcb_dev)s", "");
754""" % bcb_dev)
755 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800756
757 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
758 script.Comment("Stage 1/3")
759 _WriteRecoveryImageToBoot(script, output_zip)
760
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800761 script.AppendExtra("""
762set_stage("%(bcb_dev)s", "2/3");
763reboot_now("%(bcb_dev)s", "");
764endif;
765endif;
766""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800767
Tao Bao5d182562016-02-23 11:38:39 -0800768 script.SetProgress(1)
769 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800770 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700771 WriteMetadata(metadata, output_zip)
772
Doug Zongkerfc44a512014-08-26 13:10:25 -0700773
Dan Albert8e0178d2015-01-27 15:53:15 -0800774def WritePolicyConfig(file_name, output_zip):
775 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500776
Doug Zongker2ea21062010-04-28 16:05:21 -0700777
778def WriteMetadata(metadata, output_zip):
779 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
780 "".join(["%s=%s\n" % kv
781 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700782
Doug Zongkerfc44a512014-08-26 13:10:25 -0700783
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700784def LoadPartitionFiles(z, partition):
785 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700786 ZipFile, and return a dict of {filename: File object}."""
787 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700788 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700789 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700790 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700791 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700792 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700793 data = z.read(info.filename)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +0900794 out[fn] = common.File(fn, data, info.compress_size)
Doug Zongker1807e702012-02-28 12:21:08 -0800795 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700796
797
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700798def GetBuildProp(prop, info_dict):
799 """Return the fingerprint of the build of a given target-files info_dict."""
800 try:
801 return info_dict.get("build.prop", {})[prop]
802 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700803 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700804
Doug Zongkerfc44a512014-08-26 13:10:25 -0700805
Michael Runge4038aa82013-12-13 18:06:28 -0800806def AddToKnownPaths(filename, known_paths):
807 if filename[-1] == "/":
808 return
809 dirs = filename.split("/")[:-1]
810 while len(dirs) > 0:
811 path = "/".join(dirs)
812 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700813 break
Michael Runge4038aa82013-12-13 18:06:28 -0800814 known_paths.add(path)
815 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700816
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700817
Geremy Condra36bd3652014-02-06 19:45:10 -0800818def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700819 # TODO(tbao): We should factor out the common parts between
820 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800821 source_version = OPTIONS.source_info_dict["recovery_api_version"]
822 target_version = OPTIONS.target_info_dict["recovery_api_version"]
823
824 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700825 print("WARNING: generating edify script for a source that "
826 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700827 script = edify_generator.EdifyGenerator(
828 source_version, OPTIONS.target_info_dict,
829 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800830
Tao Bao3806c232015-07-05 21:08:33 -0700831 recovery_mount_options = OPTIONS.source_info_dict.get(
832 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700833 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
834 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Tao Bao3806c232015-07-05 21:08:33 -0700835 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700836 if source_oem_props or target_oem_props:
Tao Bao3806c232015-07-05 21:08:33 -0700837 if OPTIONS.oem_source is None:
838 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800839 if not OPTIONS.oem_no_mount:
840 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700841 oem_dict = common.LoadDictionaryFromLines(
842 open(OPTIONS.oem_source).readlines())
843
Dan Albert8b72aef2015-03-23 19:13:21 -0700844 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700845 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
846 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800847 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700848 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800849
Tao Bao5d182562016-02-23 11:38:39 -0800850 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
851 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
852 is_downgrade = long(post_timestamp) < long(pre_timestamp)
853
854 if OPTIONS.downgrade:
855 metadata["ota-downgrade"] = "yes"
856 if not is_downgrade:
857 raise RuntimeError("--downgrade specified but no downgrade detected: "
858 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
859 else:
860 if is_downgrade:
861 # Non-fatal here to allow generating such a package which may require
862 # manual work to adjust the post-timestamp. A legit use case is that we
863 # cut a new build C (after having A and B), but want to enfore the
864 # update path of A -> C -> B. Specifying --downgrade may not help since
865 # that would enforce a data wipe for C -> B update.
866 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
867 "The package may not be deployed properly. "
868 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
869 metadata["post-timestamp"] = post_timestamp
870
Geremy Condra36bd3652014-02-06 19:45:10 -0800871 device_specific = common.DeviceSpecificParams(
872 source_zip=source_zip,
873 source_version=source_version,
874 target_zip=target_zip,
875 target_version=target_version,
876 output_zip=output_zip,
877 script=script,
878 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700879 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800880
Tao Bao3e30d972016-03-15 13:20:19 -0700881 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700882 OPTIONS.source_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -0700883 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700884 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800885 metadata["pre-build"] = source_fp
886 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700887 metadata["pre-build-incremental"] = GetBuildProp(
888 "ro.build.version.incremental", OPTIONS.source_info_dict)
889 metadata["post-build-incremental"] = GetBuildProp(
890 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800891
892 source_boot = common.GetBootableImage(
893 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
894 OPTIONS.source_info_dict)
895 target_boot = common.GetBootableImage(
896 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
897 updating_boot = (not OPTIONS.two_step and
898 (source_boot.data != target_boot.data))
899
Geremy Condra36bd3652014-02-06 19:45:10 -0800900 target_recovery = common.GetBootableImage(
901 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800902
Doug Zongkerfc44a512014-08-26 13:10:25 -0700903 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
904 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700905
906 blockimgdiff_version = 1
907 if OPTIONS.info_dict:
908 blockimgdiff_version = max(
909 int(i) for i in
910 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
911
Tao Baof8acad12016-07-07 09:09:58 -0700912 # Check the first block of the source system partition for remount R/W only
913 # if the filesystem is ext4.
914 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
915 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700916 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
917 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
918 # b) the blocks listed in block map may not contain all the bytes for a given
919 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700920 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
921 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
922 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700923 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800924 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700925 version=blockimgdiff_version,
926 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700927
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700928 if HasVendorPartition(target_zip):
929 if not HasVendorPartition(source_zip):
930 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700931 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
932 OPTIONS.source_info_dict)
933 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
934 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800935
936 # Check first block of vendor partition for remount R/W only if
937 # disk type is ext4
938 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800939 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700940 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700941 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800942 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700943 version=blockimgdiff_version,
944 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700945 else:
946 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800947
Michael Rungec6e3afd2014-05-05 11:55:47 -0700948 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800949 device_specific.IncrementalOTA_Assertions()
950
951 # Two-step incremental package strategy (in chronological order,
952 # which is *not* the order in which the generated script has
953 # things):
954 #
955 # if stage is not "2/3" or "3/3":
956 # do verification on current system
957 # write recovery image to boot partition
958 # set stage to "2/3"
959 # reboot to boot partition and restart recovery
960 # else if stage is "2/3":
961 # write recovery image to recovery partition
962 # set stage to "3/3"
963 # reboot to recovery partition and restart recovery
964 # else:
965 # (stage must be "3/3")
966 # perform update:
967 # patch system files, etc.
968 # force full install of new boot image
969 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700970 # complete script normally
971 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800972
973 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700974 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800975 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700976 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800977 assert fs.fs_type.upper() == "EMMC", \
978 "two-step packages only supported on devices with EMMC /misc partitions"
979 bcb_dev = {"bcb_dev": fs.device}
980 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
981 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700982if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800983""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800984
985 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
986 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700987 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800988 script.WriteRawImage("/recovery", "recovery.img")
989 script.AppendExtra("""
990set_stage("%(bcb_dev)s", "3/3");
991reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700992else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800993""" % bcb_dev)
994
Tao Baod42e97e2016-11-30 12:11:57 -0800995 # Stage 1/3: (a) Verify the current system.
996 script.Comment("Stage 1/3")
997
Tao Bao6c55a8a2015-04-08 15:30:27 -0700998 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700999 script.Print(source_fp)
1000 script.Print(target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -07001001
Geremy Condra36bd3652014-02-06 19:45:10 -08001002 script.Print("Verifying current system...")
1003
1004 device_specific.IncrementalOTA_VerifyBegin()
1005
Tao Bao3e30d972016-03-15 13:20:19 -07001006 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1007 # patching on a device that's already on the target build will damage the
1008 # system. Because operations like move don't check the block state, they
1009 # always apply the changes unconditionally.
1010 if blockimgdiff_version <= 2:
1011 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001012 script.AssertSomeFingerprint(source_fp)
1013 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001014 script.AssertSomeThumbprint(
1015 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001016
1017 else: # blockimgdiff_version > 2
1018 if source_oem_props is None and target_oem_props is None:
1019 script.AssertSomeFingerprint(source_fp, target_fp)
1020 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001021 script.AssertSomeThumbprint(
1022 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1023 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001024 elif source_oem_props is None and target_oem_props is not None:
1025 script.AssertFingerprintOrThumbprint(
1026 source_fp,
1027 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1028 else:
1029 script.AssertFingerprintOrThumbprint(
1030 target_fp,
1031 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001032
Tao Baod8d14be2016-02-04 14:26:02 -08001033 # Check the required cache size (i.e. stashed blocks).
1034 size = []
1035 if system_diff:
1036 size.append(system_diff.required_cache)
1037 if vendor_diff:
1038 size.append(vendor_diff.required_cache)
1039
Geremy Condra36bd3652014-02-06 19:45:10 -08001040 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001041 boot_type, boot_device = common.GetTypeAndDevice(
1042 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001043 d = common.Difference(target_boot, source_boot)
1044 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001045 if d is None:
1046 include_full_boot = True
1047 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1048 else:
1049 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001050
Doug Zongkerf8340082014-08-05 10:39:37 -07001051 print "boot target: %d source: %d diff: %d" % (
1052 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001053
Doug Zongkerf8340082014-08-05 10:39:37 -07001054 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001055
Doug Zongkerf8340082014-08-05 10:39:37 -07001056 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1057 (boot_type, boot_device,
1058 source_boot.size, source_boot.sha1,
1059 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001060 size.append(target_boot.size)
1061
1062 if size:
1063 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001064
1065 device_specific.IncrementalOTA_VerifyEnd()
1066
1067 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001068 # Stage 1/3: (b) Write recovery image to /boot.
1069 _WriteRecoveryImageToBoot(script, output_zip)
1070
Geremy Condra36bd3652014-02-06 19:45:10 -08001071 script.AppendExtra("""
1072set_stage("%(bcb_dev)s", "2/3");
1073reboot_now("%(bcb_dev)s", "");
1074else
1075""" % bcb_dev)
1076
Tao Baod42e97e2016-11-30 12:11:57 -08001077 # Stage 3/3: Make changes.
1078 script.Comment("Stage 3/3")
1079
Jesse Zhao75bcea02015-01-06 10:59:53 -08001080 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001081 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001082 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001083 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001084
Geremy Condra36bd3652014-02-06 19:45:10 -08001085 script.Comment("---- start making changes here ----")
1086
1087 device_specific.IncrementalOTA_InstallBegin()
1088
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001089 system_diff.WriteScript(script, output_zip,
1090 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001091
Doug Zongkerfc44a512014-08-26 13:10:25 -07001092 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001093 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001094
1095 if OPTIONS.two_step:
1096 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1097 script.WriteRawImage("/boot", "boot.img")
1098 print "writing full boot image (forced by two-step mode)"
1099
1100 if not OPTIONS.two_step:
1101 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001102 if include_full_boot:
1103 print "boot image changed; including full."
1104 script.Print("Installing boot image...")
1105 script.WriteRawImage("/boot", "boot.img")
1106 else:
1107 # Produce the boot image by applying a patch to the current
1108 # contents of the boot partition, and write it back to the
1109 # partition.
1110 print "boot image changed; including patch."
1111 script.Print("Patching boot image...")
1112 script.ShowProgress(0.1, 10)
1113 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1114 % (boot_type, boot_device,
1115 source_boot.size, source_boot.sha1,
1116 target_boot.size, target_boot.sha1),
1117 "-",
1118 target_boot.size, target_boot.sha1,
1119 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001120 else:
1121 print "boot image unchanged; skipping."
1122
1123 # Do device-specific installation (eg, write radio image).
1124 device_specific.IncrementalOTA_InstallEnd()
1125
1126 if OPTIONS.extra_script is not None:
1127 script.AppendExtra(OPTIONS.extra_script)
1128
Doug Zongker922206e2014-03-04 13:16:24 -08001129 if OPTIONS.wipe_user_data:
1130 script.Print("Erasing user data...")
1131 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001132 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001133
Geremy Condra36bd3652014-02-06 19:45:10 -08001134 if OPTIONS.two_step:
1135 script.AppendExtra("""
1136set_stage("%(bcb_dev)s", "");
1137endif;
1138endif;
1139""" % bcb_dev)
1140
1141 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001142 # For downgrade OTAs, we prefer to use the update-binary in the source
1143 # build that is actually newer than the one in the target build.
1144 if OPTIONS.downgrade:
1145 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1146 else:
1147 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001148 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001149 WriteMetadata(metadata, output_zip)
1150
Doug Zongker32b527d2014-03-04 10:03:02 -08001151
Tao Bao9bc6bb22015-11-09 16:58:28 -08001152def WriteVerifyPackage(input_zip, output_zip):
1153 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1154
1155 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1156 recovery_mount_options = OPTIONS.info_dict.get(
1157 "recovery_mount_options")
1158 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001159 if oem_props:
Tao Bao9bc6bb22015-11-09 16:58:28 -08001160 if OPTIONS.oem_source is None:
1161 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -08001162 if not OPTIONS.oem_no_mount:
1163 script.Mount("/oem", recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001164 oem_dict = common.LoadDictionaryFromLines(
1165 open(OPTIONS.oem_source).readlines())
1166
1167 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1168 metadata = {
1169 "post-build": target_fp,
1170 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1171 OPTIONS.info_dict),
1172 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1173 }
1174
1175 device_specific = common.DeviceSpecificParams(
1176 input_zip=input_zip,
1177 input_version=OPTIONS.info_dict["recovery_api_version"],
1178 output_zip=output_zip,
1179 script=script,
1180 input_tmp=OPTIONS.input_tmp,
1181 metadata=metadata,
1182 info_dict=OPTIONS.info_dict)
1183
1184 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1185
1186 script.Print("Verifying device images against %s..." % target_fp)
1187 script.AppendExtra("")
1188
1189 script.Print("Verifying boot...")
1190 boot_img = common.GetBootableImage(
1191 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1192 boot_type, boot_device = common.GetTypeAndDevice(
1193 "/boot", OPTIONS.info_dict)
1194 script.Verify("%s:%s:%d:%s" % (
1195 boot_type, boot_device, boot_img.size, boot_img.sha1))
1196 script.AppendExtra("")
1197
1198 script.Print("Verifying recovery...")
1199 recovery_img = common.GetBootableImage(
1200 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1201 recovery_type, recovery_device = common.GetTypeAndDevice(
1202 "/recovery", OPTIONS.info_dict)
1203 script.Verify("%s:%s:%d:%s" % (
1204 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1205 script.AppendExtra("")
1206
1207 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1208 system_tgt.ResetFileMap()
1209 system_diff = common.BlockDifference("system", system_tgt, src=None)
1210 system_diff.WriteStrictVerifyScript(script)
1211
1212 if HasVendorPartition(input_zip):
1213 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1214 vendor_tgt.ResetFileMap()
1215 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1216 vendor_diff.WriteStrictVerifyScript(script)
1217
1218 # Device specific partitions, such as radio, bootloader and etc.
1219 device_specific.VerifyOTA_Assertions()
1220
1221 script.SetProgress(1.0)
1222 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001223 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001224 WriteMetadata(metadata, output_zip)
1225
1226
Tao Baoc098e9e2016-01-07 13:03:56 -08001227def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1228 source_file=None):
1229 """Generate an Android OTA package that has A/B update payload."""
1230
Alex Deymod8d96ec2016-06-10 16:38:31 -07001231 # The place where the output from the subprocess should go.
1232 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1233
Tao Baoc098e9e2016-01-07 13:03:56 -08001234 # Setup signing keys.
1235 if OPTIONS.package_key is None:
1236 OPTIONS.package_key = OPTIONS.info_dict.get(
1237 "default_system_dev_certificate",
1238 "build/target/product/security/testkey")
1239
Tao Baodea0f8b2016-06-20 17:55:06 -07001240 # A/B updater expects a signing key in RSA format. Gets the key ready for
1241 # later use in step 3, unless a payload_signer has been specified.
1242 if OPTIONS.payload_signer is None:
1243 cmd = ["openssl", "pkcs8",
1244 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1245 "-inform", "DER", "-nocrypt"]
1246 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1247 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001248 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1249 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001250 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001251
Tao Baodea0f8b2016-06-20 17:55:06 -07001252 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001253 temp_zip_file = tempfile.NamedTemporaryFile()
1254 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1255 compression=zipfile.ZIP_DEFLATED)
1256
1257 # Metadata to comply with Android OTA package format.
1258 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1259 oem_dict = None
1260 if oem_props:
1261 if OPTIONS.oem_source is None:
1262 raise common.ExternalError("OEM source required for this build")
1263 oem_dict = common.LoadDictionaryFromLines(
1264 open(OPTIONS.oem_source).readlines())
1265
1266 metadata = {
1267 "post-build": CalculateFingerprint(oem_props, oem_dict,
1268 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001269 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1270 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001271 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1272 OPTIONS.info_dict),
1273 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001274 "ota-required-cache": "0",
1275 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001276 }
1277
1278 if source_file is not None:
1279 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1280 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001281 metadata["pre-build-incremental"] = GetBuildProp(
1282 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001283
1284 # 1. Generate payload.
1285 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1286 cmd = ["brillo_update_payload", "generate",
1287 "--payload", payload_file,
1288 "--target_image", target_file]
1289 if source_file is not None:
1290 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001291 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1292 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001293 assert p1.returncode == 0, "brillo_update_payload generate failed"
1294
1295 # 2. Generate hashes of the payload and metadata files.
1296 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1297 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1298 cmd = ["brillo_update_payload", "hash",
1299 "--unsigned_payload", payload_file,
1300 "--signature_size", "256",
1301 "--metadata_hash_file", metadata_sig_file,
1302 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001303 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1304 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001305 assert p1.returncode == 0, "brillo_update_payload hash failed"
1306
1307 # 3. Sign the hashes and insert them back into the payload file.
1308 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1309 suffix=".bin")
1310 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1311 suffix=".bin")
1312 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001313 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001314 cmd = [OPTIONS.payload_signer]
1315 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001316 else:
1317 cmd = ["openssl", "pkeyutl", "-sign",
1318 "-inkey", rsa_key,
1319 "-pkeyopt", "digest:sha256"]
1320 cmd.extend(["-in", payload_sig_file,
1321 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001322 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1323 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001324 assert p1.returncode == 0, "openssl sign payload failed"
1325
1326 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001327 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001328 cmd = [OPTIONS.payload_signer]
1329 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001330 else:
1331 cmd = ["openssl", "pkeyutl", "-sign",
1332 "-inkey", rsa_key,
1333 "-pkeyopt", "digest:sha256"]
1334 cmd.extend(["-in", metadata_sig_file,
1335 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001336 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1337 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001338 assert p1.returncode == 0, "openssl sign metadata failed"
1339
1340 # 3c. Insert the signatures back into the payload file.
1341 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1342 suffix=".bin")
1343 cmd = ["brillo_update_payload", "sign",
1344 "--unsigned_payload", payload_file,
1345 "--payload", signed_payload_file,
1346 "--signature_size", "256",
1347 "--metadata_signature_file", signed_metadata_sig_file,
1348 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001349 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1350 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001351 assert p1.returncode == 0, "brillo_update_payload sign failed"
1352
Alex Deymo19241c12016-02-04 22:29:29 -08001353 # 4. Dump the signed payload properties.
1354 properties_file = common.MakeTempFile(prefix="payload-properties-",
1355 suffix=".txt")
1356 cmd = ["brillo_update_payload", "properties",
1357 "--payload", signed_payload_file,
1358 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001359 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1360 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001361 assert p1.returncode == 0, "brillo_update_payload properties failed"
1362
Tao Bao7c5dc572016-06-14 17:48:11 -07001363 if OPTIONS.wipe_user_data:
1364 with open(properties_file, "a") as f:
1365 f.write("POWERWASH=1\n")
1366 metadata["ota-wipe"] = "yes"
1367
Alex Deymo19241c12016-02-04 22:29:29 -08001368 # Add the signed payload file and properties into the zip.
1369 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001370 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1371 compress_type=zipfile.ZIP_STORED)
1372 WriteMetadata(metadata, output_zip)
1373
Tianjie Xucfa86222016-03-07 16:31:19 -08001374 # If dm-verity is supported for the device, copy contents of care_map
1375 # into A/B OTA package.
1376 if OPTIONS.info_dict.get("verity") == "true":
1377 target_zip = zipfile.ZipFile(target_file, "r")
1378 care_map_path = "META/care_map.txt"
1379 namelist = target_zip.namelist()
1380 if care_map_path in namelist:
1381 care_map_data = target_zip.read(care_map_path)
1382 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data)
1383 else:
1384 print "Warning: cannot find care map file in target_file package"
1385 common.ZipClose(target_zip)
1386
Tao Baoc098e9e2016-01-07 13:03:56 -08001387 # Sign the whole package to comply with the Android OTA package format.
1388 common.ZipClose(output_zip)
1389 SignOutput(temp_zip_file.name, output_file)
1390 temp_zip_file.close()
1391
1392
Dan Albert8b72aef2015-03-23 19:13:21 -07001393class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001394 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001395 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001396 print "Loading target..."
1397 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1398 print "Loading source..."
1399 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1400
1401 self.verbatim_targets = verbatim_targets = []
1402 self.patch_list = patch_list = []
1403 diffs = []
1404 self.renames = renames = {}
1405 known_paths = set()
1406 largest_source_size = 0
1407
1408 matching_file_cache = {}
1409 for fn, sf in source_data.items():
1410 assert fn == sf.name
1411 matching_file_cache["path:" + fn] = sf
1412 if fn in target_data.keys():
1413 AddToKnownPaths(fn, known_paths)
1414 # Only allow eligibility for filename/sha matching
1415 # if there isn't a perfect path match.
1416 if target_data.get(sf.name) is None:
1417 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1418 matching_file_cache["sha:" + sf.sha1] = sf
1419
1420 for fn in sorted(target_data.keys()):
1421 tf = target_data[fn]
1422 assert fn == tf.name
1423 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1424 if sf is not None and sf.name != tf.name:
1425 print "File has moved from " + sf.name + " to " + tf.name
1426 renames[sf.name] = tf
1427
1428 if sf is None or fn in OPTIONS.require_verbatim:
1429 # This file should be included verbatim
1430 if fn in OPTIONS.prohibit_verbatim:
1431 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1432 print "send", fn, "verbatim"
1433 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001434 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001435 if fn in target_data.keys():
1436 AddToKnownPaths(fn, known_paths)
1437 elif tf.sha1 != sf.sha1:
1438 # File is different; consider sending as a patch
1439 diffs.append(common.Difference(tf, sf))
1440 else:
1441 # Target file data identical to source (may still be renamed)
1442 pass
1443
1444 common.ComputeDifferences(diffs)
1445
1446 for diff in diffs:
1447 tf, sf, d = diff.GetPatch()
1448 path = "/".join(tf.name.split("/")[:-1])
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001449 if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001450 path not in known_paths:
1451 # patch is almost as big as the file; don't bother patching
1452 # or a patch + rename cannot take place due to the target
1453 # directory not existing
1454 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001455 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001456 if sf.name in renames:
1457 del renames[sf.name]
1458 AddToKnownPaths(tf.name, known_paths)
1459 else:
1460 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1461 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1462 largest_source_size = max(largest_source_size, sf.size)
1463
1464 self.largest_source_size = largest_source_size
1465
1466 def EmitVerification(self, script):
1467 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001468 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001469 if tf.name != sf.name:
1470 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1471 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1472 so_far += sf.size
1473 return so_far
1474
Michael Runge63f01de2014-10-28 19:24:19 -07001475 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001476 for fn, _, sha1 in self.verbatim_targets:
1477 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001478 script.FileCheck("/"+fn, sha1)
1479 for tf, _, _, _ in self.patch_list:
1480 script.FileCheck(tf.name, tf.sha1)
1481
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001482 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001483 file_list = ["/" + i[0] for i in self.verbatim_targets]
1484 file_list += ["/" + i for i in self.source_data
1485 if i not in self.target_data and i not in self.renames]
1486 file_list += list(extras)
1487 # Sort the list in descending order, which removes all the files first
1488 # before attempting to remove the folder. (Bug: 22960996)
1489 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001490
1491 def TotalPatchSize(self):
1492 return sum(i[1].size for i in self.patch_list)
1493
1494 def EmitPatches(self, script, total_patch_size, so_far):
1495 self.deferred_patch_list = deferred_patch_list = []
1496 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001497 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001498 if tf.name == "system/build.prop":
1499 deferred_patch_list.append(item)
1500 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001501 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001502 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001503 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1504 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001505 so_far += tf.size
1506 script.SetProgress(so_far / total_patch_size)
1507 return so_far
1508
1509 def EmitDeferredPatches(self, script):
1510 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001511 tf, sf, _, _ = item
1512 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1513 "patch/" + sf.name + ".p")
1514 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001515
1516 def EmitRenames(self, script):
1517 if len(self.renames) > 0:
1518 script.Print("Renaming files...")
1519 for src, tgt in self.renames.iteritems():
1520 print "Renaming " + src + " to " + tgt.name
1521 script.RenameFile(src, tgt.name)
1522
1523
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001524def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001525 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1526 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1527
Doug Zongker26e66192014-02-20 13:22:07 -08001528 if (OPTIONS.block_based and
1529 target_has_recovery_patch and
1530 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001531 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1532
Doug Zongker37974732010-09-16 17:44:38 -07001533 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1534 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001535
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001536 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001537 print("WARNING: generating edify script for a source that "
1538 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001539 script = edify_generator.EdifyGenerator(
1540 source_version, OPTIONS.target_info_dict,
1541 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001542
Tao Bao34b47bf2015-06-22 19:17:41 -07001543 recovery_mount_options = OPTIONS.source_info_dict.get(
1544 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001545 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1546 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -07001547 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001548 if source_oem_props or target_oem_props:
Michael Runge6e836112014-04-15 17:40:21 -07001549 if OPTIONS.oem_source is None:
1550 raise common.ExternalError("OEM source required for this build")
Tao Bao1bb5a182016-03-04 09:45:03 -08001551 if not OPTIONS.oem_no_mount:
1552 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001553 oem_dict = common.LoadDictionaryFromLines(
1554 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001555
Dan Albert8b72aef2015-03-23 19:13:21 -07001556 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001557 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
1558 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001559 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001560 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001561
Tao Bao5d182562016-02-23 11:38:39 -08001562 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
1563 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1564 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1565
1566 if OPTIONS.downgrade:
1567 metadata["ota-downgrade"] = "yes"
1568 if not is_downgrade:
1569 raise RuntimeError("--downgrade specified but no downgrade detected: "
1570 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
1571 else:
1572 if is_downgrade:
1573 # Non-fatal here to allow generating such a package which may require
1574 # manual work to adjust the post-timestamp. A legit use case is that we
1575 # cut a new build C (after having A and B), but want to enfore the
1576 # update path of A -> C -> B. Specifying --downgrade may not help since
1577 # that would enforce a data wipe for C -> B update.
1578 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
1579 "The package may not be deployed properly. "
1580 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
1581 metadata["post-timestamp"] = post_timestamp
1582
Doug Zongker05d3dea2009-06-22 11:32:31 -07001583 device_specific = common.DeviceSpecificParams(
1584 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001585 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001586 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001587 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001588 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001589 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001590 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001591 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001592
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001593 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001594 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001595 if HasVendorPartition(target_zip):
1596 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001597 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001598 else:
1599 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001600
Tao Bao3e30d972016-03-15 13:20:19 -07001601 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001602 OPTIONS.target_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -07001603 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001604 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001605
Tao Bao3e30d972016-03-15 13:20:19 -07001606 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001607 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001608 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001609 script.AssertSomeThumbprint(
1610 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1611 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001612 elif source_oem_props is None and target_oem_props is not None:
1613 script.AssertFingerprintOrThumbprint(
1614 source_fp,
1615 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1616 else:
1617 script.AssertFingerprintOrThumbprint(
1618 target_fp,
1619 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001620
Doug Zongker2ea21062010-04-28 16:05:21 -07001621 metadata["pre-build"] = source_fp
1622 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001623 metadata["pre-build-incremental"] = GetBuildProp(
1624 "ro.build.version.incremental", OPTIONS.source_info_dict)
1625 metadata["post-build-incremental"] = GetBuildProp(
1626 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001627
Doug Zongker55d93282011-01-25 17:03:34 -08001628 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001629 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1630 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001631 target_boot = common.GetBootableImage(
1632 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001633 updating_boot = (not OPTIONS.two_step and
1634 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001635
Doug Zongker55d93282011-01-25 17:03:34 -08001636 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001637 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1638 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001639 target_recovery = common.GetBootableImage(
1640 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001641 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001642
Doug Zongker881dd402009-09-20 14:03:55 -07001643 # Here's how we divide up the progress bar:
1644 # 0.1 for verifying the start state (PatchCheck calls)
1645 # 0.8 for applying patches (ApplyPatch calls)
1646 # 0.1 for unpacking verbatim files, symlinking, and doing the
1647 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001648
Michael Runge6e836112014-04-15 17:40:21 -07001649 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001650 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001651
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001652 # Two-step incremental package strategy (in chronological order,
1653 # which is *not* the order in which the generated script has
1654 # things):
1655 #
1656 # if stage is not "2/3" or "3/3":
1657 # do verification on current system
1658 # write recovery image to boot partition
1659 # set stage to "2/3"
1660 # reboot to boot partition and restart recovery
1661 # else if stage is "2/3":
1662 # write recovery image to recovery partition
1663 # set stage to "3/3"
1664 # reboot to recovery partition and restart recovery
1665 # else:
1666 # (stage must be "3/3")
1667 # perform update:
1668 # patch system files, etc.
1669 # force full install of new boot image
1670 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001671 # complete script normally
1672 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001673
1674 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001675 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001676 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001677 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001678 assert fs.fs_type.upper() == "EMMC", \
1679 "two-step packages only supported on devices with EMMC /misc partitions"
1680 bcb_dev = {"bcb_dev": fs.device}
1681 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1682 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001683if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001684""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001685
1686 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1687 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001688 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001689 script.WriteRawImage("/recovery", "recovery.img")
1690 script.AppendExtra("""
1691set_stage("%(bcb_dev)s", "3/3");
1692reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001693else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001694""" % bcb_dev)
1695
Tao Baod42e97e2016-11-30 12:11:57 -08001696 # Stage 1/3: (a) Verify the current system.
1697 script.Comment("Stage 1/3")
1698
Tao Bao6c55a8a2015-04-08 15:30:27 -07001699 # Dump fingerprints
1700 script.Print("Source: %s" % (source_fp,))
1701 script.Print("Target: %s" % (target_fp,))
1702
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001703 script.Print("Verifying current system...")
1704
Doug Zongkere5ff5902012-01-17 10:55:37 -08001705 device_specific.IncrementalOTA_VerifyBegin()
1706
Doug Zongker881dd402009-09-20 14:03:55 -07001707 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001708 so_far = system_diff.EmitVerification(script)
1709 if vendor_diff:
1710 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001711
Tao Baod8d14be2016-02-04 14:26:02 -08001712 size = []
1713 if system_diff.patch_list:
1714 size.append(system_diff.largest_source_size)
1715 if vendor_diff:
1716 if vendor_diff.patch_list:
1717 size.append(vendor_diff.largest_source_size)
1718
Doug Zongker5da317e2009-06-02 13:38:17 -07001719 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001720 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001721 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001722 print "boot target: %d source: %d diff: %d" % (
1723 target_boot.size, source_boot.size, len(d))
1724
Doug Zongker048e7ca2009-06-15 14:31:53 -07001725 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001726
Tao Baodd24da92015-07-29 14:09:23 -07001727 boot_type, boot_device = common.GetTypeAndDevice(
1728 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001729
1730 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1731 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001732 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001733 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001734 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001735 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001736
Tao Baod8d14be2016-02-04 14:26:02 -08001737 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001738 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001739
Doug Zongker05d3dea2009-06-22 11:32:31 -07001740 device_specific.IncrementalOTA_VerifyEnd()
1741
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001742 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001743 # Stage 1/3: (b) Write recovery image to /boot.
1744 _WriteRecoveryImageToBoot(script, output_zip)
1745
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001746 script.AppendExtra("""
1747set_stage("%(bcb_dev)s", "2/3");
1748reboot_now("%(bcb_dev)s", "");
1749else
1750""" % bcb_dev)
1751
Tao Baod42e97e2016-11-30 12:11:57 -08001752 # Stage 3/3: Make changes.
1753 script.Comment("Stage 3/3")
1754
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001755 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001756
Doug Zongkere5ff5902012-01-17 10:55:37 -08001757 device_specific.IncrementalOTA_InstallBegin()
1758
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001759 if OPTIONS.two_step:
1760 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1761 script.WriteRawImage("/boot", "boot.img")
1762 print "writing full boot image (forced by two-step mode)"
1763
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001764 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001765 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1766 if vendor_diff:
1767 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001768
Doug Zongker881dd402009-09-20 14:03:55 -07001769 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001770 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1771 if vendor_diff:
1772 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001773 if updating_boot:
1774 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001775
1776 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001777 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1778 if vendor_diff:
1779 script.Print("Patching vendor files...")
1780 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001781
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001782 if not OPTIONS.two_step:
1783 if updating_boot:
1784 # Produce the boot image by applying a patch to the current
1785 # contents of the boot partition, and write it back to the
1786 # partition.
1787 script.Print("Patching boot image...")
1788 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1789 % (boot_type, boot_device,
1790 source_boot.size, source_boot.sha1,
1791 target_boot.size, target_boot.sha1),
1792 "-",
1793 target_boot.size, target_boot.sha1,
1794 source_boot.sha1, "patch/boot.img.p")
1795 so_far += target_boot.size
1796 script.SetProgress(so_far / total_patch_size)
1797 print "boot image changed; including."
1798 else:
1799 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001800
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001801 system_items = ItemSet("system", "META/filesystem_config.txt")
1802 if vendor_diff:
1803 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1804
Doug Zongkereef39442009-04-02 12:14:19 -07001805 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001806 # Recovery is generated as a patch using both the boot image
1807 # (which contains the same linux kernel as recovery) and the file
1808 # /system/etc/recovery-resource.dat (which contains all the images
1809 # used in the recovery UI) as sources. This lets us minimize the
1810 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001811 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001812 # For older builds where recovery-resource.dat is not present, we
1813 # use only the boot image as the source.
1814
Doug Zongkerc9253822014-02-04 12:17:58 -08001815 if not target_has_recovery_patch:
1816 def output_sink(fn, data):
1817 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001818 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001819
1820 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1821 target_recovery, target_boot)
1822 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001823 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001824 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001825 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001826 else:
1827 print "recovery image unchanged; skipping."
1828
Doug Zongker881dd402009-09-20 14:03:55 -07001829 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001830
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001831 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1832 if vendor_diff:
1833 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1834
1835 temp_script = script.MakeTemporary()
1836 system_items.GetMetadata(target_zip)
1837 system_items.Get("system").SetPermissions(temp_script)
1838 if vendor_diff:
1839 vendor_items.GetMetadata(target_zip)
1840 vendor_items.Get("vendor").SetPermissions(temp_script)
1841
1842 # Note that this call will mess up the trees of Items, so make sure
1843 # we're done with them.
1844 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1845 if vendor_diff:
1846 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001847
1848 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001849 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1850
1851 # Delete all the symlinks in source that aren't in target. This
1852 # needs to happen before verbatim files are unpacked, in case a
1853 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001854
1855 # If a symlink in the source will be replaced by a regular file, we cannot
1856 # delete the symlink/file in case the package gets applied again. For such
1857 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1858 # (Bug: 23646151)
1859 replaced_symlinks = dict()
1860 if system_diff:
1861 for i in system_diff.verbatim_targets:
1862 replaced_symlinks["/%s" % (i[0],)] = i[2]
1863 if vendor_diff:
1864 for i in vendor_diff.verbatim_targets:
1865 replaced_symlinks["/%s" % (i[0],)] = i[2]
1866
1867 if system_diff:
1868 for tf in system_diff.renames.values():
1869 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1870 if vendor_diff:
1871 for tf in vendor_diff.renames.values():
1872 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1873
1874 always_delete = []
1875 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001876 for dest, link in source_symlinks:
1877 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001878 if link in replaced_symlinks:
1879 may_delete.append((link, replaced_symlinks[link]))
1880 else:
1881 always_delete.append(link)
1882 script.DeleteFiles(always_delete)
1883 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001884
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001885 if system_diff.verbatim_targets:
1886 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001887 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001888 if vendor_diff and vendor_diff.verbatim_targets:
1889 script.Print("Unpacking new vendor files...")
1890 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001891
Doug Zongkerc9253822014-02-04 12:17:58 -08001892 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001893 script.Print("Unpacking new recovery...")
1894 script.UnpackPackageDir("recovery", "/system")
1895
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001896 system_diff.EmitRenames(script)
1897 if vendor_diff:
1898 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001899
Doug Zongker05d3dea2009-06-22 11:32:31 -07001900 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001901
1902 # Create all the symlinks that don't already exist, or point to
1903 # somewhere different than what we want. Delete each symlink before
1904 # creating it, since the 'symlink' command won't overwrite.
1905 to_create = []
1906 for dest, link in target_symlinks:
1907 if link in source_symlinks_d:
1908 if dest != source_symlinks_d[link]:
1909 to_create.append((dest, link))
1910 else:
1911 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001912 script.DeleteFiles([i[1] for i in to_create])
1913 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001914
1915 # Now that the symlinks are created, we can set all the
1916 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001917 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001918
Doug Zongker881dd402009-09-20 14:03:55 -07001919 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001920 device_specific.IncrementalOTA_InstallEnd()
1921
Doug Zongker1c390a22009-05-14 19:06:36 -07001922 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001923 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001924
Doug Zongkere92f15a2011-08-26 13:46:40 -07001925 # Patch the build.prop file last, so if something fails but the
1926 # device can still come up, it appears to be the old build and will
1927 # get set the OTA package again to retry.
1928 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001929 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001930
Doug Zongker922206e2014-03-04 13:16:24 -08001931 if OPTIONS.wipe_user_data:
1932 script.Print("Erasing user data...")
1933 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001934 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001935
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001936 if OPTIONS.two_step:
1937 script.AppendExtra("""
1938set_stage("%(bcb_dev)s", "");
1939endif;
1940endif;
1941""" % bcb_dev)
1942
Michael Runge63f01de2014-10-28 19:24:19 -07001943 if OPTIONS.verify and system_diff:
1944 script.Print("Remounting and verifying system partition files...")
1945 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001946 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001947 system_diff.EmitExplicitTargetVerification(script)
1948
1949 if OPTIONS.verify and vendor_diff:
1950 script.Print("Remounting and verifying vendor partition files...")
1951 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001952 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001953 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08001954
1955 # For downgrade OTAs, we prefer to use the update-binary in the source
1956 # build that is actually newer than the one in the target build.
1957 if OPTIONS.downgrade:
1958 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1959 else:
1960 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001961
Tao Baod8d14be2016-02-04 14:26:02 -08001962 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001963 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001964
1965
1966def main(argv):
1967
1968 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001969 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001970 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001971 elif o in ("-k", "--package_key"):
1972 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001973 elif o in ("-i", "--incremental_from"):
1974 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001975 elif o == "--full_radio":
1976 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001977 elif o == "--full_bootloader":
1978 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001979 elif o in ("-w", "--wipe_user_data"):
1980 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001981 elif o == "--downgrade":
1982 OPTIONS.downgrade = True
1983 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07001984 elif o in ("-o", "--oem_settings"):
1985 OPTIONS.oem_source = a
Tao Bao8608cde2016-02-25 19:49:55 -08001986 elif o == "--oem_no_mount":
1987 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001988 elif o in ("-e", "--extra_script"):
1989 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001990 elif o in ("-a", "--aslr_mode"):
1991 if a in ("on", "On", "true", "True", "yes", "Yes"):
1992 OPTIONS.aslr_mode = True
1993 else:
1994 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001995 elif o in ("-t", "--worker_threads"):
1996 if a.isdigit():
1997 OPTIONS.worker_threads = int(a)
1998 else:
1999 raise ValueError("Cannot parse value %r for option %r - only "
2000 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002001 elif o in ("-2", "--two_step"):
2002 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002003 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002004 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002005 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002006 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002007 elif o == "--block":
2008 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002009 elif o in ("-b", "--binary"):
2010 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002011 elif o in ("--no_fallback_to_full",):
2012 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002013 elif o == "--stash_threshold":
2014 try:
2015 OPTIONS.stash_threshold = float(a)
2016 except ValueError:
2017 raise ValueError("Cannot parse value %r for option %r - expecting "
2018 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002019 elif o == "--gen_verify":
2020 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002021 elif o == "--log_diff":
2022 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002023 elif o == "--payload_signer":
2024 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002025 elif o == "--payload_signer_args":
2026 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002027 else:
2028 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002029 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002030
2031 args = common.ParseOptions(argv, __doc__,
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002032 extra_opts="b:k:i:d:we:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002033 extra_long_opts=[
2034 "board_config=",
2035 "package_key=",
2036 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002037 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002038 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002039 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002040 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07002041 "extra_script=",
2042 "worker_threads=",
2043 "aslr_mode=",
2044 "two_step",
2045 "no_signing",
2046 "block",
2047 "binary=",
2048 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002049 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002050 "verify",
2051 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002052 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002053 "gen_verify",
2054 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002055 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002056 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002057 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002058
2059 if len(args) != 2:
2060 common.Usage(__doc__)
2061 sys.exit(1)
2062
Tao Bao5d182562016-02-23 11:38:39 -08002063 if OPTIONS.downgrade:
2064 # Sanity check to enforce a data wipe.
2065 if not OPTIONS.wipe_user_data:
2066 raise ValueError("Cannot downgrade without a data wipe")
2067
2068 # We should only allow downgrading incrementals (as opposed to full).
2069 # Otherwise the device may go back from arbitrary build with this full
2070 # OTA package.
2071 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002072 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002073
Tao Baoc098e9e2016-01-07 13:03:56 -08002074 # Load the dict file from the zip directly to have a peek at the OTA type.
2075 # For packages using A/B update, unzipping is not needed.
2076 input_zip = zipfile.ZipFile(args[0], "r")
2077 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2078 common.ZipClose(input_zip)
2079
2080 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2081
2082 if ab_update:
2083 if OPTIONS.incremental_source is not None:
2084 OPTIONS.target_info_dict = OPTIONS.info_dict
2085 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2086 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2087 common.ZipClose(source_zip)
2088
2089 if OPTIONS.verbose:
2090 print "--- target info ---"
2091 common.DumpInfoDict(OPTIONS.info_dict)
2092
2093 if OPTIONS.incremental_source is not None:
2094 print "--- source info ---"
2095 common.DumpInfoDict(OPTIONS.source_info_dict)
2096
2097 WriteABOTAPackageWithBrilloScript(
2098 target_file=args[0],
2099 output_file=args[1],
2100 source_file=OPTIONS.incremental_source)
2101
2102 print "done."
2103 return
2104
Doug Zongker1c390a22009-05-14 19:06:36 -07002105 if OPTIONS.extra_script is not None:
2106 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2107
Doug Zongkereef39442009-04-02 12:14:19 -07002108 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08002109 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002110
Doug Zongkereef39442009-04-02 12:14:19 -07002111 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002112 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002113
Doug Zongker37974732010-09-16 17:44:38 -07002114 if OPTIONS.verbose:
2115 print "--- target info ---"
2116 common.DumpInfoDict(OPTIONS.info_dict)
2117
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002118 # If the caller explicitly specified the device-specific extensions
2119 # path via -s/--device_specific, use that. Otherwise, use
2120 # META/releasetools.py if it is present in the target target_files.
2121 # Otherwise, take the path of the file from 'tool_extensions' in the
2122 # info dict and look for that in the local filesystem, relative to
2123 # the current directory.
2124
Doug Zongker37974732010-09-16 17:44:38 -07002125 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002126 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2127 if os.path.exists(from_input):
2128 print "(using device-specific extensions from target_files)"
2129 OPTIONS.device_specific = from_input
2130 else:
2131 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2132
Doug Zongker37974732010-09-16 17:44:38 -07002133 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002134 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002135
Tao Baoc098e9e2016-01-07 13:03:56 -08002136 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002137 raise common.ExternalError(
2138 "--- target build has specified no recovery ---")
2139
Tao Bao767e3ac2015-11-10 12:19:19 -08002140 # Use the default key to sign the package if not specified with package_key.
2141 if not OPTIONS.no_signing:
2142 if OPTIONS.package_key is None:
2143 OPTIONS.package_key = OPTIONS.info_dict.get(
2144 "default_system_dev_certificate",
2145 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002146
Tao Bao767e3ac2015-11-10 12:19:19 -08002147 # Set up the output zip. Create a temporary zip file if signing is needed.
2148 if OPTIONS.no_signing:
2149 if os.path.exists(args[1]):
2150 os.unlink(args[1])
2151 output_zip = zipfile.ZipFile(args[1], "w",
2152 compression=zipfile.ZIP_DEFLATED)
2153 else:
2154 temp_zip_file = tempfile.NamedTemporaryFile()
2155 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2156 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002157
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002158 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002159 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002160 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002161 print "--- can't determine the cache partition size ---"
2162 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002163
Tao Bao9bc6bb22015-11-09 16:58:28 -08002164 # Generate a verify package.
2165 if OPTIONS.gen_verify:
2166 WriteVerifyPackage(input_zip, output_zip)
2167
Tao Bao767e3ac2015-11-10 12:19:19 -08002168 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002169 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002170 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002171
2172 # Generate an incremental OTA. It will fall back to generate a full OTA on
2173 # failure unless no_fallback_to_full is specified.
2174 else:
2175 print "unzipping source target-files..."
2176 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2177 OPTIONS.incremental_source)
2178 OPTIONS.target_info_dict = OPTIONS.info_dict
2179 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2180 OPTIONS.source_tmp)
2181 if OPTIONS.verbose:
2182 print "--- source info ---"
2183 common.DumpInfoDict(OPTIONS.source_info_dict)
2184 try:
2185 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002186 if OPTIONS.log_diff:
2187 out_file = open(OPTIONS.log_diff, 'w')
2188 import target_files_diff
2189 target_files_diff.recursiveDiff('',
2190 OPTIONS.source_tmp,
2191 OPTIONS.input_tmp,
2192 out_file)
2193 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002194 except ValueError:
2195 if not OPTIONS.fallback_to_full:
2196 raise
2197 print "--- failed to build incremental; falling back to full ---"
2198 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002199 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002200
Tao Bao767e3ac2015-11-10 12:19:19 -08002201 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002202
Tao Bao767e3ac2015-11-10 12:19:19 -08002203 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002204 if not OPTIONS.no_signing:
2205 SignOutput(temp_zip_file.name, args[1])
2206 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002207
Doug Zongkereef39442009-04-02 12:14:19 -07002208 print "done."
2209
2210
2211if __name__ == '__main__':
2212 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002213 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002214 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002215 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002216 print
2217 print " ERROR: %s" % (e,)
2218 print
2219 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002220 finally:
2221 common.Cleanup()