blob: 9bd7fb6c5a899a37f3612b16b68ccb44f954f562 [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
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -080053 -o (--oem_settings) <main_file[,additional_files...]>
54 Comma seperated list of files used to specify the expected OEM-specific
55 properties on the OEM partition of the intended device.
56 Multiple expected values can be used by providing multiple files.
57
Michael Runge6e836112014-04-15 17:40:21 -070058
Tao Baodf4cb0b2016-02-25 19:49:55 -080059 --oem_no_mount
60 For devices with OEM-specific properties but without an OEM partition,
61 do not mount the OEM partition in the updater-script. This should be
62 very rarely used, since it's expected to have a dedicated OEM partition
63 for OEM-specific properties. Only meaningful when -o is specified.
64
Doug Zongkerdbfaae52009-04-21 17:12:54 -070065 -w (--wipe_user_data)
66 Generate an OTA package that will wipe the user data partition
67 when installed.
68
Doug Zongker962069c2009-04-23 11:41:58 -070069 -n (--no_prereq)
70 Omit the timestamp prereq check normally included at the top of
71 the build scripts (used for developer OTA packages which
72 legitimately need to go back and forth).
73
Tao Bao4da324e2016-02-23 11:38:39 -080074 --downgrade
75 Intentionally generate an incremental OTA that updates from a newer
76 build to an older one (based on timestamp comparison). "post-timestamp"
77 will be replaced by "ota-downgrade=yes" in the metadata file. A data
78 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Baofa41fb22016-03-08 17:53:39 -080079 the metadata file. The update-binary in the source build will be used in
Tao Bao9f884e62017-02-28 11:48:48 -080080 the OTA package, unless --binary flag is specified. Please also check the
81 doc for --override_timestamp below.
82
83 --override_timestamp
84 Intentionally generate an incremental OTA that updates from a newer
85 build to an older one (based on timestamp comparison), by overriding the
86 timestamp in package metadata. This differs from --downgrade flag: we
87 know for sure this is NOT an actual downgrade case, but two builds are
88 cut in a reverse order. A legit use case is that we cut a new build C
89 (after having A and B), but want to enfore an update path of A -> C -> B.
90 Specifying --downgrade may not help since that would enforce a data wipe
91 for C -> B update. The value of "post-timestamp" will be set to the newer
92 timestamp plus one, so that the package can be pushed and applied.
Tao Bao4da324e2016-02-23 11:38:39 -080093
Doug Zongker1c390a22009-05-14 19:06:36 -070094 -e (--extra_script) <file>
95 Insert the contents of file at the end of the update script.
96
Hristo Bojinovdafb0422010-08-26 14:35:16 -070097 -a (--aslr_mode) <on|off>
98 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050099
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800100 -2 (--two_step)
101 Generate a 'two-step' OTA package, where recovery is updated
102 first, so that any changes made to the system partition are done
103 using the new recovery (new kernel, etc.).
104
Doug Zongker26e66192014-02-20 13:22:07 -0800105 --block
106 Generate a block-based OTA if possible. Will fall back to a
107 file-based OTA if the target_files is older and doesn't support
108 block-based OTAs.
109
Doug Zongker25568482014-03-03 10:21:27 -0800110 -b (--binary) <file>
111 Use the given binary as the update-binary in the output package,
112 instead of the binary in the build's target_files. Use for
113 development only.
114
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200115 -t (--worker_threads) <int>
116 Specifies the number of worker-threads that will be used when
117 generating patches for incremental updates (defaults to 3).
118
Tao Bao8dcf7382015-05-21 14:09:49 -0700119 --stash_threshold <float>
120 Specifies the threshold that will be used to compute the maximum
121 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800122
123 --gen_verify
124 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800125
126 --log_diff <file>
127 Generate a log file that shows the differences in the source and target
128 builds for an incremental package. This option is only meaningful when
129 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700130
131 --payload_signer <signer>
132 Specify the signer when signing the payload and metadata for A/B OTAs.
133 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
134 with the package private key. If the private key cannot be accessed
135 directly, a payload signer that knows how to do that should be specified.
136 The signer will be supplied with "-inkey <path_to_key>",
137 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700138
139 --payload_signer_args <args>
140 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700141"""
142
143import sys
144
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800145if sys.hexversion < 0x02070000:
146 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -0700147 sys.exit(1)
148
Doug Zongkerfc44a512014-08-26 13:10:25 -0700149import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700150import os
Tao Baoc098e9e2016-01-07 13:03:56 -0800151import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700152import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700153import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700154import zipfile
155
156import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700157import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700158import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700159
160OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700161OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700162OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700163OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700164OPTIONS.require_verbatim = set()
165OPTIONS.prohibit_verbatim = set(("system/build.prop",))
166OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700167OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700168OPTIONS.omit_prereq = False
Tao Bao4da324e2016-02-23 11:38:39 -0800169OPTIONS.downgrade = False
Tao Bao9f884e62017-02-28 11:48:48 -0800170OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700171OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700172OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700173OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
174if OPTIONS.worker_threads == 0:
175 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800176OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900177OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800178OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800179OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700180OPTIONS.oem_source = None
Tao Baodf4cb0b2016-02-25 19:49:55 -0800181OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700182OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700183OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700184OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700185# Stash size cannot exceed cache_size * threshold.
186OPTIONS.cache_size = None
187OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800188OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800189OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700190OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700191OPTIONS.payload_signer_args = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700192
Doug Zongkereef39442009-04-02 12:14:19 -0700193def MostPopularKey(d, default):
194 """Given a dict, return the key corresponding to the largest
195 value. Returns 'default' if the dict is empty."""
196 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700197 if not x:
198 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700199 x.sort()
200 return x[-1][1]
201
202
203def IsSymlink(info):
204 """Return true if the zipfile.ZipInfo object passed in represents a
205 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700206 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700207
Hristo Bojinov96be7202010-08-02 10:26:17 -0700208def IsRegular(info):
209 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700210 regular file."""
211 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700212
Michael Runge4038aa82013-12-13 18:06:28 -0800213def ClosestFileMatch(src, tgtfiles, existing):
214 """Returns the closest file match between a source file and list
215 of potential matches. The exact filename match is preferred,
216 then the sha1 is searched for, and finally a file with the same
217 basename is evaluated. Rename support in the updater-binary is
218 required for the latter checks to be used."""
219
220 result = tgtfiles.get("path:" + src.name)
221 if result is not None:
222 return result
223
224 if not OPTIONS.target_info_dict.get("update_rename_support", False):
225 return None
226
227 if src.size < 1000:
228 return None
229
230 result = tgtfiles.get("sha1:" + src.sha1)
231 if result is not None and existing.get(result.name) is None:
232 return result
233 result = tgtfiles.get("file:" + src.name.split("/")[-1])
234 if result is not None and existing.get(result.name) is None:
235 return result
236 return None
237
Dan Albert8b72aef2015-03-23 19:13:21 -0700238class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700239 def __init__(self, partition, fs_config):
240 self.partition = partition
241 self.fs_config = fs_config
242 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700243
Dan Albert8b72aef2015-03-23 19:13:21 -0700244 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700245 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700246 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700247 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700248
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700249 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700250 # The target_files contains a record of what the uid,
251 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700252 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700253
254 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700255 if not line:
256 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700257 columns = line.split()
258 name, uid, gid, mode = columns[:4]
259 selabel = None
260 capabilities = None
261
262 # After the first 4 columns, there are a series of key=value
263 # pairs. Extract out the fields we care about.
264 for element in columns[4:]:
265 key, value = element.split("=")
266 if key == "selabel":
267 selabel = value
268 if key == "capabilities":
269 capabilities = value
270
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700271 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700272 if i is not None:
273 i.uid = int(uid)
274 i.gid = int(gid)
275 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700276 i.selabel = selabel
277 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700278 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700279 i.children.sort(key=lambda i: i.name)
280
Tao Baof2cffbd2015-07-22 12:33:18 -0700281 # Set metadata for the files generated by this script. For full recovery
282 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700283 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700284 if i:
285 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700286 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700287 if i:
288 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700289
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700290
Dan Albert8b72aef2015-03-23 19:13:21 -0700291class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700292 """Items represent the metadata (user, group, mode) of files and
293 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700294 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700295 self.itemset = itemset
296 self.name = name
297 self.uid = None
298 self.gid = None
299 self.mode = None
300 self.selabel = None
301 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700302 self.is_dir = is_dir
303 self.descendants = None
304 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700305
306 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700307 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700308 self.parent.children.append(self)
309 else:
310 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700311 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700312 self.children = []
313
314 def Dump(self, indent=0):
315 if self.uid is not None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700316 print "%s%s %d %d %o" % (
317 " " * indent, self.name, self.uid, self.gid, self.mode)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700318 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700319 print "%s%s %s %s %s" % (
320 " " * indent, self.name, self.uid, self.gid, self.mode)
321 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700322 print "%s%s" % (" "*indent, self.descendants)
323 print "%s%s" % (" "*indent, self.best_subtree)
324 for i in self.children:
325 i.Dump(indent=indent+1)
326
Doug Zongkereef39442009-04-02 12:14:19 -0700327 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700328 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700329 all children and determine the best strategy for using set_perm_recursive
330 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700331 values. Recursively calls itself for all descendants.
332
Dan Albert8b72aef2015-03-23 19:13:21 -0700333 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
334 counting up all descendants of this node. (dmode or fmode may be None.)
335 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
336 fmode, selabel, capabilities) tuple that will match the most descendants of
337 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700338 """
339
Dan Albert8b72aef2015-03-23 19:13:21 -0700340 assert self.is_dir
341 key = (self.uid, self.gid, self.mode, None, self.selabel,
342 self.capabilities)
343 self.descendants = {key: 1}
344 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700345 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700346 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700347 for k, v in i.CountChildMetadata().iteritems():
348 d[k] = d.get(k, 0) + v
349 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700350 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700351 d[k] = d.get(k, 0) + 1
352
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700353 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
354 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700355
356 # First, find the (uid, gid) pair that matches the most
357 # descendants.
358 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700359 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700360 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
361 ug = MostPopularKey(ug, (0, 0))
362
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700363 # Now find the dmode, fmode, selabel, and capabilities that match
364 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700365 best_dmode = (0, 0o755)
366 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700367 best_selabel = (0, None)
368 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700369 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700370 if k[:2] != ug:
371 continue
372 if k[2] is not None and count >= best_dmode[0]:
373 best_dmode = (count, k[2])
374 if k[3] is not None and count >= best_fmode[0]:
375 best_fmode = (count, k[3])
376 if k[4] is not None and count >= best_selabel[0]:
377 best_selabel = (count, k[4])
378 if k[5] is not None and count >= best_capabilities[0]:
379 best_capabilities = (count, k[5])
380 self.best_subtree = ug + (
381 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700382
383 return d
384
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700385 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700386 """Append set_perm/set_perm_recursive commands to 'script' to
387 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700388 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700389
390 self.CountChildMetadata()
391
392 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700393 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
394 # that the current item (and all its children) have already been set to.
395 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700396 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700397 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700398 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700399 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700400 current = item.best_subtree
401
402 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700403 item.mode != current[2] or item.selabel != current[4] or \
404 item.capabilities != current[5]:
405 script.SetPermissions("/"+item.name, item.uid, item.gid,
406 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700407
408 for i in item.children:
409 recurse(i, current)
410 else:
411 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700412 item.mode != current[3] or item.selabel != current[4] or \
413 item.capabilities != current[5]:
414 script.SetPermissions("/"+item.name, item.uid, item.gid,
415 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700416
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700417 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700418
419
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700420def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
421 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700422 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800423 list of symlinks. output_zip may be None, in which case the copy is
424 skipped (but the other side effects still happen). substitute is an
425 optional dict of {output filename: contents} to be output instead of
426 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700427 """
428
429 symlinks = []
430
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700431 partition = itemset.partition
432
Doug Zongkereef39442009-04-02 12:14:19 -0700433 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700434 prefix = partition.upper() + "/"
435 if info.filename.startswith(prefix):
436 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700437 if IsSymlink(info):
438 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700439 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700440 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700441 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700442 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700443 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700444 if substitute and fn in substitute and substitute[fn] is None:
445 continue
446 if output_zip is not None:
447 if substitute and fn in substitute:
448 data = substitute[fn]
449 else:
450 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700451 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700452 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700453 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700454 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700455 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700456
457 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800458 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700459
460
Doug Zongkereef39442009-04-02 12:14:19 -0700461def SignOutput(temp_zip_name, output_zip_name):
462 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
463 pw = key_passwords[OPTIONS.package_key]
464
Doug Zongker951495f2009-08-14 12:44:19 -0700465 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
466 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700467
468
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800469def AppendAssertions(script, info_dict, oem_dicts=None):
Michael Runge6e836112014-04-15 17:40:21 -0700470 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao7fc951c2016-03-15 13:20:19 -0700471 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700472 device = GetBuildProp("ro.product.device", info_dict)
473 script.AssertDevice(device)
474 else:
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800475 if not oem_dicts:
Dan Albert8b72aef2015-03-23 19:13:21 -0700476 raise common.ExternalError(
477 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700478 for prop in oem_props.split():
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800479 values = []
480 for oem_dict in oem_dicts:
481 if oem_dict.get(prop):
482 values.append(oem_dict[prop])
483 if not values:
Dan Albert8b72aef2015-03-23 19:13:21 -0700484 raise common.ExternalError(
485 "The OEM file is missing the property %s" % prop)
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800486 script.AssertOemProperty(prop, values)
487
488
Tao Bao7fc951c2016-03-15 13:20:19 -0700489def _LoadOemDicts(script, recovery_mount_options=None):
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800490 """Returns the list of loaded OEM properties dict."""
491 oem_dicts = None
492 if OPTIONS.oem_source is None:
493 raise common.ExternalError("OEM source required for this build")
494 if not OPTIONS.oem_no_mount:
495 script.Mount("/oem", recovery_mount_options)
496 oem_dicts = []
497 for oem_file in OPTIONS.oem_source:
498 oem_dicts.append(common.LoadDictionaryFromLines(
499 open(oem_file).readlines()))
500 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700501
Doug Zongkereef39442009-04-02 12:14:19 -0700502
Tao Bao47ec5ab2016-11-30 12:11:57 -0800503def _WriteRecoveryImageToBoot(script, output_zip):
504 """Find and write recovery image to /boot in two-step OTA.
505
506 In two-step OTAs, we write recovery image to /boot as the first step so that
507 we can reboot to there and install a new recovery image to /recovery.
508 A special "recovery-two-step.img" will be preferred, which encodes the correct
509 path of "/boot". Otherwise the device may show "device is corrupt" message
510 when booting into /boot.
511
512 Fall back to using the regular recovery.img if the two-step recovery image
513 doesn't exist. Note that rebuilding the special image at this point may be
514 infeasible, because we don't have the desired boot signer and keys when
515 calling ota_from_target_files.py.
516 """
517
518 recovery_two_step_img_name = "recovery-two-step.img"
519 recovery_two_step_img_path = os.path.join(
520 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
521 if os.path.exists(recovery_two_step_img_path):
522 recovery_two_step_img = common.GetBootableImage(
523 recovery_two_step_img_name, recovery_two_step_img_name,
524 OPTIONS.input_tmp, "RECOVERY")
525 common.ZipWriteStr(
526 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
527 print "two-step package: using %s in stage 1/3" % (
528 recovery_two_step_img_name,)
529 script.WriteRawImage("/boot", recovery_two_step_img_name)
530 else:
531 print "two-step package: using recovery.img in stage 1/3"
532 # The "recovery.img" entry has been written into package earlier.
533 script.WriteRawImage("/boot", "recovery.img")
534
535
Doug Zongkerc9253822014-02-04 12:17:58 -0800536def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700537 namelist = [name for name in target_files_zip.namelist()]
538 return ("SYSTEM/recovery-from-boot.p" in namelist or
539 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700540
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700541def HasVendorPartition(target_files_zip):
542 try:
543 target_files_zip.getinfo("VENDOR/")
544 return True
545 except KeyError:
546 return False
547
Michael Runge6e836112014-04-15 17:40:21 -0700548def GetOemProperty(name, oem_props, oem_dict, info_dict):
549 if oem_props is not None and name in oem_props:
550 return oem_dict[name]
551 return GetBuildProp(name, info_dict)
552
553
554def CalculateFingerprint(oem_props, oem_dict, info_dict):
555 if oem_props is None:
556 return GetBuildProp("ro.build.fingerprint", info_dict)
557 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700558 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
559 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
560 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
561 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700562
Doug Zongkerfc44a512014-08-26 13:10:25 -0700563
Doug Zongker3c84f562014-07-31 11:06:30 -0700564def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700565 # Return an image object (suitable for passing to BlockImageDiff)
566 # for the 'which' partition (most be "system" or "vendor"). If a
567 # prebuilt image and file map are found in tmpdir they are used,
568 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700569
570 assert which in ("system", "vendor")
571
572 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700573 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
574 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700575 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700576 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700577
578 else:
579 print "building %s.img from target-files" % (which,)
580
581 # This is an 'old' target-files, which does not contain images
582 # already built. Build them.
583
Doug Zongkerfc44a512014-08-26 13:10:25 -0700584 mappath = tempfile.mkstemp()[1]
585 OPTIONS.tempfiles.append(mappath)
586
Doug Zongker3c84f562014-07-31 11:06:30 -0700587 import add_img_to_target_files
588 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700589 path = add_img_to_target_files.BuildSystem(
590 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700591 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700592 path = add_img_to_target_files.BuildVendor(
593 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700594
Tao Baoff777812015-05-12 11:42:31 -0700595 # Bug: http://b/20939131
596 # In ext4 filesystems, block 0 might be changed even being mounted
597 # R/O. We add it to clobbered_blocks so that it will be written to the
598 # target unconditionally. Note that they are still part of care_map.
599 clobbered_blocks = "0"
600
601 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700602
603
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700604def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700605 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700606 # be installed on top of. For now, we expect the API just won't
607 # change very often. Similarly for fstab, it might have changed
608 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700609 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700610
Michael Runge6e836112014-04-15 17:40:21 -0700611 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700612 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800613 oem_dicts = None
614 if oem_props:
615 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -0700616
Dan Albert8b72aef2015-03-23 19:13:21 -0700617 metadata = {
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800618 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700619 OPTIONS.info_dict),
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800620 "pre-device": GetOemProperty("ro.product.device", oem_props,
621 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -0700622 OPTIONS.info_dict),
623 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
624 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700625
Doug Zongker05d3dea2009-06-22 11:32:31 -0700626 device_specific = common.DeviceSpecificParams(
627 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700628 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700629 output_zip=output_zip,
630 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700631 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700632 metadata=metadata,
633 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700634
Doug Zongkerc9253822014-02-04 12:17:58 -0800635 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800636 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800637
Tao Baob4cfca52016-02-04 14:26:02 -0800638 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
639
Doug Zongker962069c2009-04-23 11:41:58 -0700640 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700641 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700642 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
643 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700644
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800645 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700646 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800647
648 # Two-step package strategy (in chronological order, which is *not*
649 # the order in which the generated script has things):
650 #
651 # if stage is not "2/3" or "3/3":
652 # write recovery image to boot partition
653 # set stage to "2/3"
654 # reboot to boot partition and restart recovery
655 # else if stage is "2/3":
656 # write recovery image to recovery partition
657 # set stage to "3/3"
658 # reboot to recovery partition and restart recovery
659 # else:
660 # (stage must be "3/3")
661 # set stage to ""
662 # do normal full package installation:
663 # wipe and install system, boot image, etc.
664 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700665 # complete script normally
666 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800667
668 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
669 OPTIONS.input_tmp, "RECOVERY")
670 if OPTIONS.two_step:
671 if not OPTIONS.info_dict.get("multistage_support", None):
672 assert False, "two-step packages not supported by this build"
673 fs = OPTIONS.info_dict["fstab"]["/misc"]
674 assert fs.fs_type.upper() == "EMMC", \
675 "two-step packages only supported on devices with EMMC /misc partitions"
676 bcb_dev = {"bcb_dev": fs.device}
677 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
678 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700679if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800680""" % bcb_dev)
Tao Bao47ec5ab2016-11-30 12:11:57 -0800681
682 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
683 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800684 script.WriteRawImage("/recovery", "recovery.img")
685 script.AppendExtra("""
686set_stage("%(bcb_dev)s", "3/3");
687reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700688else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800689""" % bcb_dev)
690
Tao Bao47ec5ab2016-11-30 12:11:57 -0800691 # Stage 3/3: Make changes.
692 script.Comment("Stage 3/3")
693
Tao Bao6c55a8a2015-04-08 15:30:27 -0700694 # Dump fingerprints
695 script.Print("Target: %s" % CalculateFingerprint(
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800696 oem_props, oem_dicts and oem_dicts[0], OPTIONS.info_dict))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700697
Doug Zongkere5ff5902012-01-17 10:55:37 -0800698 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700699
Doug Zongker01ce19c2014-02-04 13:48:15 -0800700 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700701
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700702 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800703 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700704 if HasVendorPartition(input_zip):
705 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700706
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400707 # Place a copy of file_contexts.bin into the OTA package which will be used
708 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700709 if "selinux_fc" in OPTIONS.info_dict:
710 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500711
Michael Runge7cd99ba2014-10-22 17:21:48 -0700712 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
713
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700714 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700715 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800716
Doug Zongker26e66192014-02-20 13:22:07 -0800717 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700718 # Full OTA is done as an "incremental" against an empty source
719 # image. This has the effect of writing new data from the package
720 # to the entire partition, but lets us reuse the updater code that
721 # writes incrementals to do it.
722 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
723 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700724 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700725 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800726 else:
727 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700728 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800729 if not has_recovery_patch:
730 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800731 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700732
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700733 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800734 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700735
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700736 boot_img = common.GetBootableImage(
737 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800738
Doug Zongker91a99c22014-05-09 13:15:01 -0700739 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800740 def output_sink(fn, data):
741 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700742 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800743
744 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
745 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700746
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700747 system_items.GetMetadata(input_zip)
748 system_items.Get("system").SetPermissions(script)
749
750 if HasVendorPartition(input_zip):
751 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
752 script.ShowProgress(0.1, 0)
753
754 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700755 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
756 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700757 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700758 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700759 else:
760 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700761 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700762 script.UnpackPackageDir("vendor", "/vendor")
763
764 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
765 script.MakeSymlinks(symlinks)
766
767 vendor_items.GetMetadata(input_zip)
768 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700769
Doug Zongker37974732010-09-16 17:44:38 -0700770 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700771 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700772
Doug Zongker01ce19c2014-02-04 13:48:15 -0800773 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700774 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700775
Doug Zongker01ce19c2014-02-04 13:48:15 -0800776 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700777 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700778
Doug Zongker1c390a22009-05-14 19:06:36 -0700779 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700780 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700781
Doug Zongker14833602010-02-02 13:12:04 -0800782 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800783
Doug Zongker922206e2014-03-04 13:16:24 -0800784 if OPTIONS.wipe_user_data:
785 script.ShowProgress(0.1, 10)
786 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700787
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800788 if OPTIONS.two_step:
789 script.AppendExtra("""
790set_stage("%(bcb_dev)s", "");
791""" % bcb_dev)
792 script.AppendExtra("else\n")
Tao Bao47ec5ab2016-11-30 12:11:57 -0800793
794 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
795 script.Comment("Stage 1/3")
796 _WriteRecoveryImageToBoot(script, output_zip)
797
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800798 script.AppendExtra("""
799set_stage("%(bcb_dev)s", "2/3");
800reboot_now("%(bcb_dev)s", "");
801endif;
802endif;
803""" % bcb_dev)
Tao Baob4cfca52016-02-04 14:26:02 -0800804
Tao Bao4da324e2016-02-23 11:38:39 -0800805 script.SetProgress(1)
806 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -0800807 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700808 WriteMetadata(metadata, output_zip)
809
Doug Zongkerfc44a512014-08-26 13:10:25 -0700810
Dan Albert8e0178d2015-01-27 15:53:15 -0800811def WritePolicyConfig(file_name, output_zip):
812 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500813
Doug Zongker2ea21062010-04-28 16:05:21 -0700814
815def WriteMetadata(metadata, output_zip):
816 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
817 "".join(["%s=%s\n" % kv
818 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700819
Doug Zongkerfc44a512014-08-26 13:10:25 -0700820
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700821def LoadPartitionFiles(z, partition):
822 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700823 ZipFile, and return a dict of {filename: File object}."""
824 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700825 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700826 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700827 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700828 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700829 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700830 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700831 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800832 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700833
834
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700835def GetBuildProp(prop, info_dict):
836 """Return the fingerprint of the build of a given target-files info_dict."""
837 try:
838 return info_dict.get("build.prop", {})[prop]
839 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700840 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700841
Doug Zongkerfc44a512014-08-26 13:10:25 -0700842
Michael Runge4038aa82013-12-13 18:06:28 -0800843def AddToKnownPaths(filename, known_paths):
844 if filename[-1] == "/":
845 return
846 dirs = filename.split("/")[:-1]
847 while len(dirs) > 0:
848 path = "/".join(dirs)
849 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700850 break
Michael Runge4038aa82013-12-13 18:06:28 -0800851 known_paths.add(path)
852 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700853
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700854
Tao Bao99c17252017-02-07 11:21:17 -0800855def HandleDowngradeMetadata(metadata):
856 # Only incremental OTAs are allowed to reach here.
857 assert OPTIONS.incremental_source is not None
858
859 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
860 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
861 is_downgrade = long(post_timestamp) < long(pre_timestamp)
862
863 if OPTIONS.downgrade:
Tao Bao99c17252017-02-07 11:21:17 -0800864 if not is_downgrade:
865 raise RuntimeError("--downgrade specified but no downgrade detected: "
866 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao9f884e62017-02-28 11:48:48 -0800867 metadata["ota-downgrade"] = "yes"
868 elif OPTIONS.timestamp:
869 if not is_downgrade:
870 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
871 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
872 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Bao99c17252017-02-07 11:21:17 -0800873 else:
874 if is_downgrade:
Tao Bao9f884e62017-02-28 11:48:48 -0800875 raise RuntimeError("Downgrade detected based on timestamp check: "
876 "pre: %s, post: %s. Need to specify --timestamp OR "
877 "--downgrade to allow building the incremental." % (
878 pre_timestamp, post_timestamp))
Tao Bao99c17252017-02-07 11:21:17 -0800879 metadata["post-timestamp"] = post_timestamp
880
881
Geremy Condra36bd3652014-02-06 19:45:10 -0800882def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700883 # TODO(tbao): We should factor out the common parts between
884 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800885 source_version = OPTIONS.source_info_dict["recovery_api_version"]
886 target_version = OPTIONS.target_info_dict["recovery_api_version"]
887
888 if source_version == 0:
889 print ("WARNING: generating edify script for a source that "
890 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700891 script = edify_generator.EdifyGenerator(
892 source_version, OPTIONS.target_info_dict,
893 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800894
Tao Bao3806c232015-07-05 21:08:33 -0700895 recovery_mount_options = OPTIONS.source_info_dict.get(
896 "recovery_mount_options")
Tao Bao7fc951c2016-03-15 13:20:19 -0700897 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
898 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800899 oem_dicts = None
Tao Bao7fc951c2016-03-15 13:20:19 -0700900 if source_oem_props or target_oem_props:
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800901 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700902
Dan Albert8b72aef2015-03-23 19:13:21 -0700903 metadata = {
Tao Bao7fc951c2016-03-15 13:20:19 -0700904 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800905 oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700906 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -0800907 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700908 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800909
Tao Bao99c17252017-02-07 11:21:17 -0800910 HandleDowngradeMetadata(metadata)
Tao Bao4da324e2016-02-23 11:38:39 -0800911
Geremy Condra36bd3652014-02-06 19:45:10 -0800912 device_specific = common.DeviceSpecificParams(
913 source_zip=source_zip,
914 source_version=source_version,
915 target_zip=target_zip,
916 target_version=target_version,
917 output_zip=output_zip,
918 script=script,
919 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700920 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800921
Tao Bao7fc951c2016-03-15 13:20:19 -0700922 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700923 OPTIONS.source_info_dict)
Tao Bao7fc951c2016-03-15 13:20:19 -0700924 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700925 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800926 metadata["pre-build"] = source_fp
927 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700928 metadata["pre-build-incremental"] = GetBuildProp(
929 "ro.build.version.incremental", OPTIONS.source_info_dict)
930 metadata["post-build-incremental"] = GetBuildProp(
931 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800932
933 source_boot = common.GetBootableImage(
934 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
935 OPTIONS.source_info_dict)
936 target_boot = common.GetBootableImage(
937 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
938 updating_boot = (not OPTIONS.two_step and
939 (source_boot.data != target_boot.data))
940
Geremy Condra36bd3652014-02-06 19:45:10 -0800941 target_recovery = common.GetBootableImage(
942 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800943
Doug Zongkerfc44a512014-08-26 13:10:25 -0700944 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
945 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700946
947 blockimgdiff_version = 1
948 if OPTIONS.info_dict:
949 blockimgdiff_version = max(
950 int(i) for i in
951 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
952
Tao Baof8acad12016-07-07 09:09:58 -0700953 # Check the first block of the source system partition for remount R/W only
954 # if the filesystem is ext4.
955 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
956 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700957 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
958 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
959 # b) the blocks listed in block map may not contain all the bytes for a given
960 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700961 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
962 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
963 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700964 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800965 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700966 version=blockimgdiff_version,
967 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700968
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700969 if HasVendorPartition(target_zip):
970 if not HasVendorPartition(source_zip):
971 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700972 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
973 OPTIONS.source_info_dict)
974 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
975 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800976
977 # Check first block of vendor partition for remount R/W only if
978 # disk type is ext4
979 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baob4cfca52016-02-04 14:26:02 -0800980 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700981 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700982 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800983 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700984 version=blockimgdiff_version,
985 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700986 else:
987 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800988
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -0800989 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800990 device_specific.IncrementalOTA_Assertions()
991
992 # Two-step incremental package strategy (in chronological order,
993 # which is *not* the order in which the generated script has
994 # things):
995 #
996 # if stage is not "2/3" or "3/3":
997 # do verification on current system
998 # write recovery image to boot partition
999 # set stage to "2/3"
1000 # reboot to boot partition and restart recovery
1001 # else if stage is "2/3":
1002 # write recovery image to recovery partition
1003 # set stage to "3/3"
1004 # reboot to recovery partition and restart recovery
1005 # else:
1006 # (stage must be "3/3")
1007 # perform update:
1008 # patch system files, etc.
1009 # force full install of new boot image
1010 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001011 # complete script normally
1012 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001013
1014 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001015 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -08001016 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001017 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001018 assert fs.fs_type.upper() == "EMMC", \
1019 "two-step packages only supported on devices with EMMC /misc partitions"
1020 bcb_dev = {"bcb_dev": fs.device}
1021 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1022 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001023if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001024""" % bcb_dev)
Tao Bao47ec5ab2016-11-30 12:11:57 -08001025
1026 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1027 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001028 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001029 script.WriteRawImage("/recovery", "recovery.img")
1030 script.AppendExtra("""
1031set_stage("%(bcb_dev)s", "3/3");
1032reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001033else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001034""" % bcb_dev)
1035
Tao Bao47ec5ab2016-11-30 12:11:57 -08001036 # Stage 1/3: (a) Verify the current system.
1037 script.Comment("Stage 1/3")
1038
Tao Bao6c55a8a2015-04-08 15:30:27 -07001039 # Dump fingerprints
Tao Bao7fc951c2016-03-15 13:20:19 -07001040 script.Print("Source: %s" % (source_fp,))
1041 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001042
Geremy Condra36bd3652014-02-06 19:45:10 -08001043 script.Print("Verifying current system...")
1044
1045 device_specific.IncrementalOTA_VerifyBegin()
1046
Tao Bao7fc951c2016-03-15 13:20:19 -07001047 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1048 # patching on a device that's already on the target build will damage the
1049 # system. Because operations like move don't check the block state, they
1050 # always apply the changes unconditionally.
1051 if blockimgdiff_version <= 2:
1052 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001053 script.AssertSomeFingerprint(source_fp)
1054 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001055 script.AssertSomeThumbprint(
1056 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao7fc951c2016-03-15 13:20:19 -07001057
1058 else: # blockimgdiff_version > 2
1059 if source_oem_props is None and target_oem_props is None:
1060 script.AssertSomeFingerprint(source_fp, target_fp)
1061 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001062 script.AssertSomeThumbprint(
1063 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1064 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao7fc951c2016-03-15 13:20:19 -07001065 elif source_oem_props is None and target_oem_props is not None:
1066 script.AssertFingerprintOrThumbprint(
1067 source_fp,
1068 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1069 else:
1070 script.AssertFingerprintOrThumbprint(
1071 target_fp,
1072 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001073
Tao Baob4cfca52016-02-04 14:26:02 -08001074 # Check the required cache size (i.e. stashed blocks).
1075 size = []
1076 if system_diff:
1077 size.append(system_diff.required_cache)
1078 if vendor_diff:
1079 size.append(vendor_diff.required_cache)
1080
Geremy Condra36bd3652014-02-06 19:45:10 -08001081 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001082 boot_type, boot_device = common.GetTypeAndDevice(
1083 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001084 d = common.Difference(target_boot, source_boot)
1085 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001086 if d is None:
1087 include_full_boot = True
1088 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1089 else:
1090 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001091
Doug Zongkerf8340082014-08-05 10:39:37 -07001092 print "boot target: %d source: %d diff: %d" % (
1093 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001094
Doug Zongkerf8340082014-08-05 10:39:37 -07001095 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001096
Doug Zongkerf8340082014-08-05 10:39:37 -07001097 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1098 (boot_type, boot_device,
1099 source_boot.size, source_boot.sha1,
1100 target_boot.size, target_boot.sha1))
Tao Baob4cfca52016-02-04 14:26:02 -08001101 size.append(target_boot.size)
1102
1103 if size:
1104 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001105
1106 device_specific.IncrementalOTA_VerifyEnd()
1107
1108 if OPTIONS.two_step:
Tao Bao47ec5ab2016-11-30 12:11:57 -08001109 # Stage 1/3: (b) Write recovery image to /boot.
1110 _WriteRecoveryImageToBoot(script, output_zip)
1111
Geremy Condra36bd3652014-02-06 19:45:10 -08001112 script.AppendExtra("""
1113set_stage("%(bcb_dev)s", "2/3");
1114reboot_now("%(bcb_dev)s", "");
1115else
1116""" % bcb_dev)
1117
Tao Bao47ec5ab2016-11-30 12:11:57 -08001118 # Stage 3/3: Make changes.
1119 script.Comment("Stage 3/3")
1120
Jesse Zhao75bcea02015-01-06 10:59:53 -08001121 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001122 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001123 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001124 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001125
Geremy Condra36bd3652014-02-06 19:45:10 -08001126 script.Comment("---- start making changes here ----")
1127
1128 device_specific.IncrementalOTA_InstallBegin()
1129
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001130 system_diff.WriteScript(script, output_zip,
1131 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001132
Doug Zongkerfc44a512014-08-26 13:10:25 -07001133 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001134 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001135
1136 if OPTIONS.two_step:
1137 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1138 script.WriteRawImage("/boot", "boot.img")
1139 print "writing full boot image (forced by two-step mode)"
1140
1141 if not OPTIONS.two_step:
1142 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001143 if include_full_boot:
1144 print "boot image changed; including full."
1145 script.Print("Installing boot image...")
1146 script.WriteRawImage("/boot", "boot.img")
1147 else:
1148 # Produce the boot image by applying a patch to the current
1149 # contents of the boot partition, and write it back to the
1150 # partition.
1151 print "boot image changed; including patch."
1152 script.Print("Patching boot image...")
1153 script.ShowProgress(0.1, 10)
1154 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1155 % (boot_type, boot_device,
1156 source_boot.size, source_boot.sha1,
1157 target_boot.size, target_boot.sha1),
1158 "-",
1159 target_boot.size, target_boot.sha1,
1160 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001161 else:
1162 print "boot image unchanged; skipping."
1163
1164 # Do device-specific installation (eg, write radio image).
1165 device_specific.IncrementalOTA_InstallEnd()
1166
1167 if OPTIONS.extra_script is not None:
1168 script.AppendExtra(OPTIONS.extra_script)
1169
Doug Zongker922206e2014-03-04 13:16:24 -08001170 if OPTIONS.wipe_user_data:
1171 script.Print("Erasing user data...")
1172 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001173 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001174
Geremy Condra36bd3652014-02-06 19:45:10 -08001175 if OPTIONS.two_step:
1176 script.AppendExtra("""
1177set_stage("%(bcb_dev)s", "");
1178endif;
1179endif;
1180""" % bcb_dev)
1181
1182 script.SetProgress(1)
Tao Baofa41fb22016-03-08 17:53:39 -08001183 # For downgrade OTAs, we prefer to use the update-binary in the source
1184 # build that is actually newer than the one in the target build.
1185 if OPTIONS.downgrade:
1186 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1187 else:
1188 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001189 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001190 WriteMetadata(metadata, output_zip)
1191
Doug Zongker32b527d2014-03-04 10:03:02 -08001192
Tao Bao9bc6bb22015-11-09 16:58:28 -08001193def WriteVerifyPackage(input_zip, output_zip):
1194 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1195
1196 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1197 recovery_mount_options = OPTIONS.info_dict.get(
1198 "recovery_mount_options")
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001199 oem_dicts = None
1200 if oem_props:
1201 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001202
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001203 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
1204 OPTIONS.info_dict)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001205 metadata = {
1206 "post-build": target_fp,
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001207 "pre-device": GetOemProperty("ro.product.device", oem_props,
1208 oem_dicts and oem_dicts[0],
Tao Bao9bc6bb22015-11-09 16:58:28 -08001209 OPTIONS.info_dict),
1210 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1211 }
1212
1213 device_specific = common.DeviceSpecificParams(
1214 input_zip=input_zip,
1215 input_version=OPTIONS.info_dict["recovery_api_version"],
1216 output_zip=output_zip,
1217 script=script,
1218 input_tmp=OPTIONS.input_tmp,
1219 metadata=metadata,
1220 info_dict=OPTIONS.info_dict)
1221
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001222 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001223
1224 script.Print("Verifying device images against %s..." % target_fp)
1225 script.AppendExtra("")
1226
1227 script.Print("Verifying boot...")
1228 boot_img = common.GetBootableImage(
1229 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1230 boot_type, boot_device = common.GetTypeAndDevice(
1231 "/boot", OPTIONS.info_dict)
1232 script.Verify("%s:%s:%d:%s" % (
1233 boot_type, boot_device, boot_img.size, boot_img.sha1))
1234 script.AppendExtra("")
1235
1236 script.Print("Verifying recovery...")
1237 recovery_img = common.GetBootableImage(
1238 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1239 recovery_type, recovery_device = common.GetTypeAndDevice(
1240 "/recovery", OPTIONS.info_dict)
1241 script.Verify("%s:%s:%d:%s" % (
1242 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1243 script.AppendExtra("")
1244
1245 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1246 system_tgt.ResetFileMap()
1247 system_diff = common.BlockDifference("system", system_tgt, src=None)
1248 system_diff.WriteStrictVerifyScript(script)
1249
1250 if HasVendorPartition(input_zip):
1251 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1252 vendor_tgt.ResetFileMap()
1253 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1254 vendor_diff.WriteStrictVerifyScript(script)
1255
1256 # Device specific partitions, such as radio, bootloader and etc.
1257 device_specific.VerifyOTA_Assertions()
1258
1259 script.SetProgress(1.0)
1260 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001261 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001262 WriteMetadata(metadata, output_zip)
1263
1264
Tao Baoc098e9e2016-01-07 13:03:56 -08001265def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1266 source_file=None):
1267 """Generate an Android OTA package that has A/B update payload."""
1268
1269 # Setup signing keys.
1270 if OPTIONS.package_key is None:
1271 OPTIONS.package_key = OPTIONS.info_dict.get(
1272 "default_system_dev_certificate",
1273 "build/target/product/security/testkey")
1274
Tao Baodea0f8b2016-06-20 17:55:06 -07001275 # A/B updater expects a signing key in RSA format. Gets the key ready for
1276 # later use in step 3, unless a payload_signer has been specified.
1277 if OPTIONS.payload_signer is None:
1278 cmd = ["openssl", "pkcs8",
1279 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1280 "-inform", "DER", "-nocrypt"]
1281 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1282 cmd.extend(["-out", rsa_key])
1283 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1284 p1.wait()
1285 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001286
Tao Baodea0f8b2016-06-20 17:55:06 -07001287 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001288 temp_zip_file = tempfile.NamedTemporaryFile()
1289 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1290 compression=zipfile.ZIP_DEFLATED)
1291
1292 # Metadata to comply with Android OTA package format.
1293 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001294 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001295 if oem_props:
Tao Bao7fc951c2016-03-15 13:20:19 -07001296 oem_dicts = _LoadOemDicts(None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001297
1298 metadata = {
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001299 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001300 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001301 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1302 OPTIONS.info_dict),
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001303 "pre-device": GetOemProperty("ro.product.device", oem_props,
1304 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001305 OPTIONS.info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001306 "ota-required-cache": "0",
1307 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001308 }
1309
1310 if source_file is not None:
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001311 metadata["pre-build"] = CalculateFingerprint(oem_props,
1312 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001313 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001314 metadata["pre-build-incremental"] = GetBuildProp(
1315 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001316
Tao Bao99c17252017-02-07 11:21:17 -08001317 HandleDowngradeMetadata(metadata)
1318 else:
1319 metadata["post-timestamp"] = GetBuildProp(
1320 "ro.build.date.utc", OPTIONS.info_dict)
1321
Tao Baoc098e9e2016-01-07 13:03:56 -08001322 # 1. Generate payload.
1323 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1324 cmd = ["brillo_update_payload", "generate",
1325 "--payload", payload_file,
1326 "--target_image", target_file]
1327 if source_file is not None:
1328 cmd.extend(["--source_image", source_file])
Tao Baod21de1b2017-10-03 14:17:57 -07001329 if OPTIONS.downgrade:
1330 max_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1331 else:
1332 max_timestamp = metadata["post-timestamp"]
1333 cmd.extend(["--max_timestamp", max_timestamp])
Tao Baoc098e9e2016-01-07 13:03:56 -08001334 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1335 p1.wait()
1336 assert p1.returncode == 0, "brillo_update_payload generate failed"
1337
1338 # 2. Generate hashes of the payload and metadata files.
1339 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1340 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1341 cmd = ["brillo_update_payload", "hash",
1342 "--unsigned_payload", payload_file,
1343 "--signature_size", "256",
1344 "--metadata_hash_file", metadata_sig_file,
1345 "--payload_hash_file", payload_sig_file]
1346 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1347 p1.wait()
1348 assert p1.returncode == 0, "brillo_update_payload hash failed"
1349
1350 # 3. Sign the hashes and insert them back into the payload file.
1351 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1352 suffix=".bin")
1353 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1354 suffix=".bin")
1355 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001356 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001357 cmd = [OPTIONS.payload_signer]
1358 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001359 else:
1360 cmd = ["openssl", "pkeyutl", "-sign",
1361 "-inkey", rsa_key,
1362 "-pkeyopt", "digest:sha256"]
1363 cmd.extend(["-in", payload_sig_file,
1364 "-out", signed_payload_sig_file])
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001365
Tao Baoc098e9e2016-01-07 13:03:56 -08001366 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1367 p1.wait()
1368 assert p1.returncode == 0, "openssl sign payload failed"
1369
1370 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001371 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001372 cmd = [OPTIONS.payload_signer]
1373 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001374 else:
1375 cmd = ["openssl", "pkeyutl", "-sign",
1376 "-inkey", rsa_key,
1377 "-pkeyopt", "digest:sha256"]
1378 cmd.extend(["-in", metadata_sig_file,
1379 "-out", signed_metadata_sig_file])
Tao Baoc098e9e2016-01-07 13:03:56 -08001380 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1381 p1.wait()
1382 assert p1.returncode == 0, "openssl sign metadata failed"
1383
1384 # 3c. Insert the signatures back into the payload file.
1385 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1386 suffix=".bin")
1387 cmd = ["brillo_update_payload", "sign",
1388 "--unsigned_payload", payload_file,
1389 "--payload", signed_payload_file,
1390 "--signature_size", "256",
1391 "--metadata_signature_file", signed_metadata_sig_file,
1392 "--payload_signature_file", signed_payload_sig_file]
1393 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1394 p1.wait()
1395 assert p1.returncode == 0, "brillo_update_payload sign failed"
1396
Alex Deymo19241c12016-02-04 22:29:29 -08001397 # 4. Dump the signed payload properties.
1398 properties_file = common.MakeTempFile(prefix="payload-properties-",
1399 suffix=".txt")
1400 cmd = ["brillo_update_payload", "properties",
1401 "--payload", signed_payload_file,
1402 "--properties_file", properties_file]
1403 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1404 p1.wait()
1405 assert p1.returncode == 0, "brillo_update_payload properties failed"
1406
Tao Bao38ca0be2016-06-14 17:48:11 -07001407 if OPTIONS.wipe_user_data:
1408 with open(properties_file, "a") as f:
1409 f.write("POWERWASH=1\n")
1410 metadata["ota-wipe"] = "yes"
1411
Alex Deymo19241c12016-02-04 22:29:29 -08001412 # Add the signed payload file and properties into the zip.
1413 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001414 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1415 compress_type=zipfile.ZIP_STORED)
1416 WriteMetadata(metadata, output_zip)
1417
Tianjie Xucfa86222016-03-07 16:31:19 -08001418 # If dm-verity is supported for the device, copy contents of care_map
1419 # into A/B OTA package.
1420 if OPTIONS.info_dict.get("verity") == "true":
1421 target_zip = zipfile.ZipFile(target_file, "r")
1422 care_map_path = "META/care_map.txt"
1423 namelist = target_zip.namelist()
1424 if care_map_path in namelist:
1425 care_map_data = target_zip.read(care_map_path)
1426 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data)
1427 else:
1428 print "Warning: cannot find care map file in target_file package"
1429 common.ZipClose(target_zip)
1430
Tao Baoc098e9e2016-01-07 13:03:56 -08001431 # Sign the whole package to comply with the Android OTA package format.
1432 common.ZipClose(output_zip)
1433 SignOutput(temp_zip_file.name, output_file)
1434 temp_zip_file.close()
1435
1436
Dan Albert8b72aef2015-03-23 19:13:21 -07001437class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001438 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001439 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001440 print "Loading target..."
1441 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1442 print "Loading source..."
1443 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1444
1445 self.verbatim_targets = verbatim_targets = []
1446 self.patch_list = patch_list = []
1447 diffs = []
1448 self.renames = renames = {}
1449 known_paths = set()
1450 largest_source_size = 0
1451
1452 matching_file_cache = {}
1453 for fn, sf in source_data.items():
1454 assert fn == sf.name
1455 matching_file_cache["path:" + fn] = sf
1456 if fn in target_data.keys():
1457 AddToKnownPaths(fn, known_paths)
1458 # Only allow eligibility for filename/sha matching
1459 # if there isn't a perfect path match.
1460 if target_data.get(sf.name) is None:
1461 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1462 matching_file_cache["sha:" + sf.sha1] = sf
1463
1464 for fn in sorted(target_data.keys()):
1465 tf = target_data[fn]
1466 assert fn == tf.name
1467 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1468 if sf is not None and sf.name != tf.name:
1469 print "File has moved from " + sf.name + " to " + tf.name
1470 renames[sf.name] = tf
1471
1472 if sf is None or fn in OPTIONS.require_verbatim:
1473 # This file should be included verbatim
1474 if fn in OPTIONS.prohibit_verbatim:
1475 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1476 print "send", fn, "verbatim"
1477 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001478 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001479 if fn in target_data.keys():
1480 AddToKnownPaths(fn, known_paths)
1481 elif tf.sha1 != sf.sha1:
1482 # File is different; consider sending as a patch
1483 diffs.append(common.Difference(tf, sf))
1484 else:
1485 # Target file data identical to source (may still be renamed)
1486 pass
1487
1488 common.ComputeDifferences(diffs)
1489
1490 for diff in diffs:
1491 tf, sf, d = diff.GetPatch()
1492 path = "/".join(tf.name.split("/")[:-1])
1493 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1494 path not in known_paths:
1495 # patch is almost as big as the file; don't bother patching
1496 # or a patch + rename cannot take place due to the target
1497 # directory not existing
1498 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001499 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001500 if sf.name in renames:
1501 del renames[sf.name]
1502 AddToKnownPaths(tf.name, known_paths)
1503 else:
1504 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1505 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1506 largest_source_size = max(largest_source_size, sf.size)
1507
1508 self.largest_source_size = largest_source_size
1509
1510 def EmitVerification(self, script):
1511 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001512 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001513 if tf.name != sf.name:
1514 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1515 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1516 so_far += sf.size
1517 return so_far
1518
Michael Runge63f01de2014-10-28 19:24:19 -07001519 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001520 for fn, _, sha1 in self.verbatim_targets:
1521 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001522 script.FileCheck("/"+fn, sha1)
1523 for tf, _, _, _ in self.patch_list:
1524 script.FileCheck(tf.name, tf.sha1)
1525
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001526 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001527 file_list = ["/" + i[0] for i in self.verbatim_targets]
1528 file_list += ["/" + i for i in self.source_data
1529 if i not in self.target_data and i not in self.renames]
1530 file_list += list(extras)
1531 # Sort the list in descending order, which removes all the files first
1532 # before attempting to remove the folder. (Bug: 22960996)
1533 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001534
1535 def TotalPatchSize(self):
1536 return sum(i[1].size for i in self.patch_list)
1537
1538 def EmitPatches(self, script, total_patch_size, so_far):
1539 self.deferred_patch_list = deferred_patch_list = []
1540 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001541 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001542 if tf.name == "system/build.prop":
1543 deferred_patch_list.append(item)
1544 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001545 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001546 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001547 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1548 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001549 so_far += tf.size
1550 script.SetProgress(so_far / total_patch_size)
1551 return so_far
1552
1553 def EmitDeferredPatches(self, script):
1554 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001555 tf, sf, _, _ = item
1556 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1557 "patch/" + sf.name + ".p")
1558 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001559
1560 def EmitRenames(self, script):
1561 if len(self.renames) > 0:
1562 script.Print("Renaming files...")
1563 for src, tgt in self.renames.iteritems():
1564 print "Renaming " + src + " to " + tgt.name
1565 script.RenameFile(src, tgt.name)
1566
1567
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001568def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001569 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1570 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1571
Doug Zongker26e66192014-02-20 13:22:07 -08001572 if (OPTIONS.block_based and
1573 target_has_recovery_patch and
1574 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001575 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1576
Doug Zongker37974732010-09-16 17:44:38 -07001577 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1578 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001579
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001580 if source_version == 0:
1581 print ("WARNING: generating edify script for a source that "
1582 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001583 script = edify_generator.EdifyGenerator(
1584 source_version, OPTIONS.target_info_dict,
1585 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001586
Tao Bao34b47bf2015-06-22 19:17:41 -07001587 recovery_mount_options = OPTIONS.source_info_dict.get(
1588 "recovery_mount_options")
Tao Bao7fc951c2016-03-15 13:20:19 -07001589 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1590 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001591 oem_dicts = None
Tao Bao7fc951c2016-03-15 13:20:19 -07001592 if source_oem_props or target_oem_props:
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001593 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -07001594
Dan Albert8b72aef2015-03-23 19:13:21 -07001595 metadata = {
Tao Bao7fc951c2016-03-15 13:20:19 -07001596 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001597 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001598 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001599 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001600 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001601
Tao Bao99c17252017-02-07 11:21:17 -08001602 HandleDowngradeMetadata(metadata)
Tao Bao4da324e2016-02-23 11:38:39 -08001603
Doug Zongker05d3dea2009-06-22 11:32:31 -07001604 device_specific = common.DeviceSpecificParams(
1605 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001606 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001607 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001608 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001609 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001610 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001611 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001612 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001613
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001614 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001615 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001616 if HasVendorPartition(target_zip):
1617 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001618 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001619 else:
1620 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001621
Tao Bao7fc951c2016-03-15 13:20:19 -07001622 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001623 OPTIONS.target_info_dict)
Tao Bao7fc951c2016-03-15 13:20:19 -07001624 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001625 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001626
Tao Bao7fc951c2016-03-15 13:20:19 -07001627 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001628 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao7fc951c2016-03-15 13:20:19 -07001629 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001630 script.AssertSomeThumbprint(
1631 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1632 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao7fc951c2016-03-15 13:20:19 -07001633 elif source_oem_props is None and target_oem_props is not None:
1634 script.AssertFingerprintOrThumbprint(
1635 source_fp,
1636 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1637 else:
1638 script.AssertFingerprintOrThumbprint(
1639 target_fp,
1640 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001641
Doug Zongker2ea21062010-04-28 16:05:21 -07001642 metadata["pre-build"] = source_fp
1643 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001644 metadata["pre-build-incremental"] = GetBuildProp(
1645 "ro.build.version.incremental", OPTIONS.source_info_dict)
1646 metadata["post-build-incremental"] = GetBuildProp(
1647 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001648
Doug Zongker55d93282011-01-25 17:03:34 -08001649 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001650 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1651 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001652 target_boot = common.GetBootableImage(
1653 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001654 updating_boot = (not OPTIONS.two_step and
1655 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001656
Doug Zongker55d93282011-01-25 17:03:34 -08001657 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001658 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1659 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001660 target_recovery = common.GetBootableImage(
1661 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001662 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001663
Doug Zongker881dd402009-09-20 14:03:55 -07001664 # Here's how we divide up the progress bar:
1665 # 0.1 for verifying the start state (PatchCheck calls)
1666 # 0.8 for applying patches (ApplyPatch calls)
1667 # 0.1 for unpacking verbatim files, symlinking, and doing the
1668 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001669
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001670 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001671 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001672
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001673 # Two-step incremental package strategy (in chronological order,
1674 # which is *not* the order in which the generated script has
1675 # things):
1676 #
1677 # if stage is not "2/3" or "3/3":
1678 # do verification on current system
1679 # write recovery image to boot partition
1680 # set stage to "2/3"
1681 # reboot to boot partition and restart recovery
1682 # else if stage is "2/3":
1683 # write recovery image to recovery partition
1684 # set stage to "3/3"
1685 # reboot to recovery partition and restart recovery
1686 # else:
1687 # (stage must be "3/3")
1688 # perform update:
1689 # patch system files, etc.
1690 # force full install of new boot image
1691 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001692 # complete script normally
1693 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001694
1695 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001696 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001697 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001698 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001699 assert fs.fs_type.upper() == "EMMC", \
1700 "two-step packages only supported on devices with EMMC /misc partitions"
1701 bcb_dev = {"bcb_dev": fs.device}
1702 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1703 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001704if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001705""" % bcb_dev)
Tao Bao47ec5ab2016-11-30 12:11:57 -08001706
1707 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1708 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001709 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001710 script.WriteRawImage("/recovery", "recovery.img")
1711 script.AppendExtra("""
1712set_stage("%(bcb_dev)s", "3/3");
1713reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001714else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001715""" % bcb_dev)
1716
Tao Bao47ec5ab2016-11-30 12:11:57 -08001717 # Stage 1/3: (a) Verify the current system.
1718 script.Comment("Stage 1/3")
1719
Tao Bao6c55a8a2015-04-08 15:30:27 -07001720 # Dump fingerprints
1721 script.Print("Source: %s" % (source_fp,))
1722 script.Print("Target: %s" % (target_fp,))
1723
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001724 script.Print("Verifying current system...")
1725
Doug Zongkere5ff5902012-01-17 10:55:37 -08001726 device_specific.IncrementalOTA_VerifyBegin()
1727
Doug Zongker881dd402009-09-20 14:03:55 -07001728 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001729 so_far = system_diff.EmitVerification(script)
1730 if vendor_diff:
1731 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001732
Tao Baob4cfca52016-02-04 14:26:02 -08001733 size = []
1734 if system_diff.patch_list:
1735 size.append(system_diff.largest_source_size)
1736 if vendor_diff:
1737 if vendor_diff.patch_list:
1738 size.append(vendor_diff.largest_source_size)
1739
Doug Zongker5da317e2009-06-02 13:38:17 -07001740 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001741 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001742 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001743 print "boot target: %d source: %d diff: %d" % (
1744 target_boot.size, source_boot.size, len(d))
1745
Doug Zongker048e7ca2009-06-15 14:31:53 -07001746 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001747
Tao Baodd24da92015-07-29 14:09:23 -07001748 boot_type, boot_device = common.GetTypeAndDevice(
1749 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001750
1751 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1752 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001753 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001754 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001755 so_far += source_boot.size
Tao Baob4cfca52016-02-04 14:26:02 -08001756 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001757
Tao Baob4cfca52016-02-04 14:26:02 -08001758 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001759 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001760
Doug Zongker05d3dea2009-06-22 11:32:31 -07001761 device_specific.IncrementalOTA_VerifyEnd()
1762
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001763 if OPTIONS.two_step:
Tao Bao47ec5ab2016-11-30 12:11:57 -08001764 # Stage 1/3: (b) Write recovery image to /boot.
1765 _WriteRecoveryImageToBoot(script, output_zip)
1766
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001767 script.AppendExtra("""
1768set_stage("%(bcb_dev)s", "2/3");
1769reboot_now("%(bcb_dev)s", "");
1770else
1771""" % bcb_dev)
1772
Tao Bao47ec5ab2016-11-30 12:11:57 -08001773 # Stage 3/3: Make changes.
1774 script.Comment("Stage 3/3")
1775
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001776 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001777
Doug Zongkere5ff5902012-01-17 10:55:37 -08001778 device_specific.IncrementalOTA_InstallBegin()
1779
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001780 if OPTIONS.two_step:
1781 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1782 script.WriteRawImage("/boot", "boot.img")
1783 print "writing full boot image (forced by two-step mode)"
1784
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001785 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001786 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1787 if vendor_diff:
1788 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001789
Doug Zongker881dd402009-09-20 14:03:55 -07001790 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001791 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1792 if vendor_diff:
1793 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001794 if updating_boot:
1795 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001796
1797 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001798 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1799 if vendor_diff:
1800 script.Print("Patching vendor files...")
1801 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001802
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001803 if not OPTIONS.two_step:
1804 if updating_boot:
1805 # Produce the boot image by applying a patch to the current
1806 # contents of the boot partition, and write it back to the
1807 # partition.
1808 script.Print("Patching boot image...")
1809 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1810 % (boot_type, boot_device,
1811 source_boot.size, source_boot.sha1,
1812 target_boot.size, target_boot.sha1),
1813 "-",
1814 target_boot.size, target_boot.sha1,
1815 source_boot.sha1, "patch/boot.img.p")
1816 so_far += target_boot.size
1817 script.SetProgress(so_far / total_patch_size)
1818 print "boot image changed; including."
1819 else:
1820 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001821
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001822 system_items = ItemSet("system", "META/filesystem_config.txt")
1823 if vendor_diff:
1824 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1825
Doug Zongkereef39442009-04-02 12:14:19 -07001826 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001827 # Recovery is generated as a patch using both the boot image
1828 # (which contains the same linux kernel as recovery) and the file
1829 # /system/etc/recovery-resource.dat (which contains all the images
1830 # used in the recovery UI) as sources. This lets us minimize the
1831 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001832 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001833 # For older builds where recovery-resource.dat is not present, we
1834 # use only the boot image as the source.
1835
Doug Zongkerc9253822014-02-04 12:17:58 -08001836 if not target_has_recovery_patch:
1837 def output_sink(fn, data):
1838 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001839 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001840
1841 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1842 target_recovery, target_boot)
1843 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001844 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001845 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001846 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001847 else:
1848 print "recovery image unchanged; skipping."
1849
Doug Zongker881dd402009-09-20 14:03:55 -07001850 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001851
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001852 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1853 if vendor_diff:
1854 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1855
1856 temp_script = script.MakeTemporary()
1857 system_items.GetMetadata(target_zip)
1858 system_items.Get("system").SetPermissions(temp_script)
1859 if vendor_diff:
1860 vendor_items.GetMetadata(target_zip)
1861 vendor_items.Get("vendor").SetPermissions(temp_script)
1862
1863 # Note that this call will mess up the trees of Items, so make sure
1864 # we're done with them.
1865 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1866 if vendor_diff:
1867 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001868
1869 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001870 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1871
1872 # Delete all the symlinks in source that aren't in target. This
1873 # needs to happen before verbatim files are unpacked, in case a
1874 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001875
1876 # If a symlink in the source will be replaced by a regular file, we cannot
1877 # delete the symlink/file in case the package gets applied again. For such
1878 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1879 # (Bug: 23646151)
1880 replaced_symlinks = dict()
1881 if system_diff:
1882 for i in system_diff.verbatim_targets:
1883 replaced_symlinks["/%s" % (i[0],)] = i[2]
1884 if vendor_diff:
1885 for i in vendor_diff.verbatim_targets:
1886 replaced_symlinks["/%s" % (i[0],)] = i[2]
1887
1888 if system_diff:
1889 for tf in system_diff.renames.values():
1890 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1891 if vendor_diff:
1892 for tf in vendor_diff.renames.values():
1893 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1894
1895 always_delete = []
1896 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001897 for dest, link in source_symlinks:
1898 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001899 if link in replaced_symlinks:
1900 may_delete.append((link, replaced_symlinks[link]))
1901 else:
1902 always_delete.append(link)
1903 script.DeleteFiles(always_delete)
1904 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001905
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001906 if system_diff.verbatim_targets:
1907 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001908 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001909 if vendor_diff and vendor_diff.verbatim_targets:
1910 script.Print("Unpacking new vendor files...")
1911 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001912
Doug Zongkerc9253822014-02-04 12:17:58 -08001913 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001914 script.Print("Unpacking new recovery...")
1915 script.UnpackPackageDir("recovery", "/system")
1916
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001917 system_diff.EmitRenames(script)
1918 if vendor_diff:
1919 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001920
Doug Zongker05d3dea2009-06-22 11:32:31 -07001921 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001922
1923 # Create all the symlinks that don't already exist, or point to
1924 # somewhere different than what we want. Delete each symlink before
1925 # creating it, since the 'symlink' command won't overwrite.
1926 to_create = []
1927 for dest, link in target_symlinks:
1928 if link in source_symlinks_d:
1929 if dest != source_symlinks_d[link]:
1930 to_create.append((dest, link))
1931 else:
1932 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001933 script.DeleteFiles([i[1] for i in to_create])
1934 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001935
1936 # Now that the symlinks are created, we can set all the
1937 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001938 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001939
Doug Zongker881dd402009-09-20 14:03:55 -07001940 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001941 device_specific.IncrementalOTA_InstallEnd()
1942
Doug Zongker1c390a22009-05-14 19:06:36 -07001943 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001944 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001945
Doug Zongkere92f15a2011-08-26 13:46:40 -07001946 # Patch the build.prop file last, so if something fails but the
1947 # device can still come up, it appears to be the old build and will
1948 # get set the OTA package again to retry.
1949 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001950 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001951
Doug Zongker922206e2014-03-04 13:16:24 -08001952 if OPTIONS.wipe_user_data:
1953 script.Print("Erasing user data...")
1954 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001955 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001956
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001957 if OPTIONS.two_step:
1958 script.AppendExtra("""
1959set_stage("%(bcb_dev)s", "");
1960endif;
1961endif;
1962""" % bcb_dev)
1963
Michael Runge63f01de2014-10-28 19:24:19 -07001964 if OPTIONS.verify and system_diff:
1965 script.Print("Remounting and verifying system partition files...")
1966 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001967 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001968 system_diff.EmitExplicitTargetVerification(script)
1969
1970 if OPTIONS.verify and vendor_diff:
1971 script.Print("Remounting and verifying vendor partition files...")
1972 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001973 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001974 vendor_diff.EmitExplicitTargetVerification(script)
Tao Baofa41fb22016-03-08 17:53:39 -08001975
1976 # For downgrade OTAs, we prefer to use the update-binary in the source
1977 # build that is actually newer than the one in the target build.
1978 if OPTIONS.downgrade:
1979 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1980 else:
1981 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001982
Tao Baob4cfca52016-02-04 14:26:02 -08001983 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001984 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001985
1986
1987def main(argv):
1988
1989 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001990 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001991 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001992 elif o in ("-k", "--package_key"):
1993 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001994 elif o in ("-i", "--incremental_from"):
1995 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001996 elif o == "--full_radio":
1997 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001998 elif o == "--full_bootloader":
1999 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002000 elif o in ("-w", "--wipe_user_data"):
2001 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07002002 elif o in ("-n", "--no_prereq"):
2003 OPTIONS.omit_prereq = True
Tao Bao4da324e2016-02-23 11:38:39 -08002004 elif o == "--downgrade":
2005 OPTIONS.downgrade = True
2006 OPTIONS.wipe_user_data = True
Tao Bao9f884e62017-02-28 11:48:48 -08002007 elif o == "--override_timestamp":
2008 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07002009 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08002010 OPTIONS.oem_source = a.split(',')
Tao Baodf4cb0b2016-02-25 19:49:55 -08002011 elif o == "--oem_no_mount":
2012 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002013 elif o in ("-e", "--extra_script"):
2014 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07002015 elif o in ("-a", "--aslr_mode"):
2016 if a in ("on", "On", "true", "True", "yes", "Yes"):
2017 OPTIONS.aslr_mode = True
2018 else:
2019 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002020 elif o in ("-t", "--worker_threads"):
2021 if a.isdigit():
2022 OPTIONS.worker_threads = int(a)
2023 else:
2024 raise ValueError("Cannot parse value %r for option %r - only "
2025 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002026 elif o in ("-2", "--two_step"):
2027 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002028 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002029 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002030 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002031 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002032 elif o == "--block":
2033 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002034 elif o in ("-b", "--binary"):
2035 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002036 elif o in ("--no_fallback_to_full",):
2037 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002038 elif o == "--stash_threshold":
2039 try:
2040 OPTIONS.stash_threshold = float(a)
2041 except ValueError:
2042 raise ValueError("Cannot parse value %r for option %r - expecting "
2043 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002044 elif o == "--gen_verify":
2045 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002046 elif o == "--log_diff":
2047 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002048 elif o == "--payload_signer":
2049 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002050 elif o == "--payload_signer_args":
2051 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002052 else:
2053 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002054 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002055
2056 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07002057 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002058 extra_long_opts=[
2059 "board_config=",
2060 "package_key=",
2061 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002062 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002063 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002064 "wipe_user_data",
2065 "no_prereq",
Tao Bao4da324e2016-02-23 11:38:39 -08002066 "downgrade",
Tao Bao9f884e62017-02-28 11:48:48 -08002067 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002068 "extra_script=",
2069 "worker_threads=",
2070 "aslr_mode=",
2071 "two_step",
2072 "no_signing",
2073 "block",
2074 "binary=",
2075 "oem_settings=",
Tao Baodf4cb0b2016-02-25 19:49:55 -08002076 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002077 "verify",
2078 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002079 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002080 "gen_verify",
2081 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002082 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002083 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002084 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002085
2086 if len(args) != 2:
2087 common.Usage(__doc__)
2088 sys.exit(1)
2089
Tao Bao4da324e2016-02-23 11:38:39 -08002090 if OPTIONS.downgrade:
2091 # Sanity check to enforce a data wipe.
2092 if not OPTIONS.wipe_user_data:
2093 raise ValueError("Cannot downgrade without a data wipe")
2094
2095 # We should only allow downgrading incrementals (as opposed to full).
2096 # Otherwise the device may go back from arbitrary build with this full
2097 # OTA package.
2098 if OPTIONS.incremental_source is None:
2099 raise ValueError("Cannot generate downgradable full OTAs - consider"
2100 "using --omit_prereq?")
2101
Tao Bao9f884e62017-02-28 11:48:48 -08002102 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
2103 "Cannot have --downgrade AND --override_timestamp both"
2104
Tao Baoc098e9e2016-01-07 13:03:56 -08002105 # Load the dict file from the zip directly to have a peek at the OTA type.
2106 # For packages using A/B update, unzipping is not needed.
2107 input_zip = zipfile.ZipFile(args[0], "r")
2108 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2109 common.ZipClose(input_zip)
2110
2111 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2112
2113 if ab_update:
2114 if OPTIONS.incremental_source is not None:
2115 OPTIONS.target_info_dict = OPTIONS.info_dict
2116 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2117 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2118 common.ZipClose(source_zip)
2119
2120 if OPTIONS.verbose:
2121 print "--- target info ---"
2122 common.DumpInfoDict(OPTIONS.info_dict)
2123
2124 if OPTIONS.incremental_source is not None:
2125 print "--- source info ---"
2126 common.DumpInfoDict(OPTIONS.source_info_dict)
2127
2128 WriteABOTAPackageWithBrilloScript(
2129 target_file=args[0],
2130 output_file=args[1],
2131 source_file=OPTIONS.incremental_source)
2132
2133 print "done."
2134 return
2135
Doug Zongker1c390a22009-05-14 19:06:36 -07002136 if OPTIONS.extra_script is not None:
2137 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2138
Doug Zongkereef39442009-04-02 12:14:19 -07002139 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08002140 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002141
Doug Zongkereef39442009-04-02 12:14:19 -07002142 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002143 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002144
Doug Zongker37974732010-09-16 17:44:38 -07002145 if OPTIONS.verbose:
2146 print "--- target info ---"
2147 common.DumpInfoDict(OPTIONS.info_dict)
2148
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002149 # If the caller explicitly specified the device-specific extensions
2150 # path via -s/--device_specific, use that. Otherwise, use
2151 # META/releasetools.py if it is present in the target target_files.
2152 # Otherwise, take the path of the file from 'tool_extensions' in the
2153 # info dict and look for that in the local filesystem, relative to
2154 # the current directory.
2155
Doug Zongker37974732010-09-16 17:44:38 -07002156 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002157 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2158 if os.path.exists(from_input):
2159 print "(using device-specific extensions from target_files)"
2160 OPTIONS.device_specific = from_input
2161 else:
2162 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2163
Doug Zongker37974732010-09-16 17:44:38 -07002164 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002165 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002166
Tao Baoc098e9e2016-01-07 13:03:56 -08002167 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002168 raise common.ExternalError(
2169 "--- target build has specified no recovery ---")
2170
Tao Bao767e3ac2015-11-10 12:19:19 -08002171 # Use the default key to sign the package if not specified with package_key.
2172 if not OPTIONS.no_signing:
2173 if OPTIONS.package_key is None:
2174 OPTIONS.package_key = OPTIONS.info_dict.get(
2175 "default_system_dev_certificate",
2176 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002177
Tao Bao767e3ac2015-11-10 12:19:19 -08002178 # Set up the output zip. Create a temporary zip file if signing is needed.
2179 if OPTIONS.no_signing:
2180 if os.path.exists(args[1]):
2181 os.unlink(args[1])
2182 output_zip = zipfile.ZipFile(args[1], "w",
2183 compression=zipfile.ZIP_DEFLATED)
2184 else:
2185 temp_zip_file = tempfile.NamedTemporaryFile()
2186 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2187 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002188
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002189 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002190 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002191 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002192 print "--- can't determine the cache partition size ---"
2193 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002194
Tao Bao9bc6bb22015-11-09 16:58:28 -08002195 # Generate a verify package.
2196 if OPTIONS.gen_verify:
2197 WriteVerifyPackage(input_zip, output_zip)
2198
Tao Bao767e3ac2015-11-10 12:19:19 -08002199 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002200 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002201 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002202
2203 # Generate an incremental OTA. It will fall back to generate a full OTA on
2204 # failure unless no_fallback_to_full is specified.
2205 else:
2206 print "unzipping source target-files..."
2207 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2208 OPTIONS.incremental_source)
2209 OPTIONS.target_info_dict = OPTIONS.info_dict
2210 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2211 OPTIONS.source_tmp)
2212 if OPTIONS.verbose:
2213 print "--- source info ---"
2214 common.DumpInfoDict(OPTIONS.source_info_dict)
2215 try:
2216 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002217 if OPTIONS.log_diff:
2218 out_file = open(OPTIONS.log_diff, 'w')
2219 import target_files_diff
2220 target_files_diff.recursiveDiff('',
2221 OPTIONS.source_tmp,
2222 OPTIONS.input_tmp,
2223 out_file)
2224 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002225 except ValueError:
2226 if not OPTIONS.fallback_to_full:
2227 raise
2228 print "--- failed to build incremental; falling back to full ---"
2229 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002230 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002231
Tao Bao767e3ac2015-11-10 12:19:19 -08002232 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002233
Tao Bao767e3ac2015-11-10 12:19:19 -08002234 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002235 if not OPTIONS.no_signing:
2236 SignOutput(temp_zip_file.name, args[1])
2237 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002238
Doug Zongkereef39442009-04-02 12:14:19 -07002239 print "done."
2240
2241
2242if __name__ == '__main__':
2243 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002244 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002245 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002246 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002247 print
2248 print " ERROR: %s" % (e,)
2249 print
2250 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002251 finally:
2252 common.Cleanup()