blob: 199e700cc653a3c0eb4755b78a87635e84f4e006 [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])
1329 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1330 p1.wait()
1331 assert p1.returncode == 0, "brillo_update_payload generate failed"
1332
1333 # 2. Generate hashes of the payload and metadata files.
1334 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1335 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1336 cmd = ["brillo_update_payload", "hash",
1337 "--unsigned_payload", payload_file,
1338 "--signature_size", "256",
1339 "--metadata_hash_file", metadata_sig_file,
1340 "--payload_hash_file", payload_sig_file]
1341 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1342 p1.wait()
1343 assert p1.returncode == 0, "brillo_update_payload hash failed"
1344
1345 # 3. Sign the hashes and insert them back into the payload file.
1346 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1347 suffix=".bin")
1348 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1349 suffix=".bin")
1350 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001351 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001352 cmd = [OPTIONS.payload_signer]
1353 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001354 else:
1355 cmd = ["openssl", "pkeyutl", "-sign",
1356 "-inkey", rsa_key,
1357 "-pkeyopt", "digest:sha256"]
1358 cmd.extend(["-in", payload_sig_file,
1359 "-out", signed_payload_sig_file])
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001360
Tao Baoc098e9e2016-01-07 13:03:56 -08001361 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1362 p1.wait()
1363 assert p1.returncode == 0, "openssl sign payload failed"
1364
1365 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001366 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001367 cmd = [OPTIONS.payload_signer]
1368 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001369 else:
1370 cmd = ["openssl", "pkeyutl", "-sign",
1371 "-inkey", rsa_key,
1372 "-pkeyopt", "digest:sha256"]
1373 cmd.extend(["-in", metadata_sig_file,
1374 "-out", signed_metadata_sig_file])
Tao Baoc098e9e2016-01-07 13:03:56 -08001375 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1376 p1.wait()
1377 assert p1.returncode == 0, "openssl sign metadata failed"
1378
1379 # 3c. Insert the signatures back into the payload file.
1380 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1381 suffix=".bin")
1382 cmd = ["brillo_update_payload", "sign",
1383 "--unsigned_payload", payload_file,
1384 "--payload", signed_payload_file,
1385 "--signature_size", "256",
1386 "--metadata_signature_file", signed_metadata_sig_file,
1387 "--payload_signature_file", signed_payload_sig_file]
1388 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1389 p1.wait()
1390 assert p1.returncode == 0, "brillo_update_payload sign failed"
1391
Alex Deymo19241c12016-02-04 22:29:29 -08001392 # 4. Dump the signed payload properties.
1393 properties_file = common.MakeTempFile(prefix="payload-properties-",
1394 suffix=".txt")
1395 cmd = ["brillo_update_payload", "properties",
1396 "--payload", signed_payload_file,
1397 "--properties_file", properties_file]
1398 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1399 p1.wait()
1400 assert p1.returncode == 0, "brillo_update_payload properties failed"
1401
Tao Bao38ca0be2016-06-14 17:48:11 -07001402 if OPTIONS.wipe_user_data:
1403 with open(properties_file, "a") as f:
1404 f.write("POWERWASH=1\n")
1405 metadata["ota-wipe"] = "yes"
1406
Alex Deymo19241c12016-02-04 22:29:29 -08001407 # Add the signed payload file and properties into the zip.
1408 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001409 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1410 compress_type=zipfile.ZIP_STORED)
1411 WriteMetadata(metadata, output_zip)
1412
Tianjie Xucfa86222016-03-07 16:31:19 -08001413 # If dm-verity is supported for the device, copy contents of care_map
1414 # into A/B OTA package.
1415 if OPTIONS.info_dict.get("verity") == "true":
1416 target_zip = zipfile.ZipFile(target_file, "r")
1417 care_map_path = "META/care_map.txt"
1418 namelist = target_zip.namelist()
1419 if care_map_path in namelist:
1420 care_map_data = target_zip.read(care_map_path)
1421 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data)
1422 else:
1423 print "Warning: cannot find care map file in target_file package"
1424 common.ZipClose(target_zip)
1425
Tao Baoc098e9e2016-01-07 13:03:56 -08001426 # Sign the whole package to comply with the Android OTA package format.
1427 common.ZipClose(output_zip)
1428 SignOutput(temp_zip_file.name, output_file)
1429 temp_zip_file.close()
1430
1431
Dan Albert8b72aef2015-03-23 19:13:21 -07001432class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001433 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001434 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001435 print "Loading target..."
1436 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1437 print "Loading source..."
1438 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1439
1440 self.verbatim_targets = verbatim_targets = []
1441 self.patch_list = patch_list = []
1442 diffs = []
1443 self.renames = renames = {}
1444 known_paths = set()
1445 largest_source_size = 0
1446
1447 matching_file_cache = {}
1448 for fn, sf in source_data.items():
1449 assert fn == sf.name
1450 matching_file_cache["path:" + fn] = sf
1451 if fn in target_data.keys():
1452 AddToKnownPaths(fn, known_paths)
1453 # Only allow eligibility for filename/sha matching
1454 # if there isn't a perfect path match.
1455 if target_data.get(sf.name) is None:
1456 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1457 matching_file_cache["sha:" + sf.sha1] = sf
1458
1459 for fn in sorted(target_data.keys()):
1460 tf = target_data[fn]
1461 assert fn == tf.name
1462 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1463 if sf is not None and sf.name != tf.name:
1464 print "File has moved from " + sf.name + " to " + tf.name
1465 renames[sf.name] = tf
1466
1467 if sf is None or fn in OPTIONS.require_verbatim:
1468 # This file should be included verbatim
1469 if fn in OPTIONS.prohibit_verbatim:
1470 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1471 print "send", fn, "verbatim"
1472 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001473 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001474 if fn in target_data.keys():
1475 AddToKnownPaths(fn, known_paths)
1476 elif tf.sha1 != sf.sha1:
1477 # File is different; consider sending as a patch
1478 diffs.append(common.Difference(tf, sf))
1479 else:
1480 # Target file data identical to source (may still be renamed)
1481 pass
1482
1483 common.ComputeDifferences(diffs)
1484
1485 for diff in diffs:
1486 tf, sf, d = diff.GetPatch()
1487 path = "/".join(tf.name.split("/")[:-1])
1488 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1489 path not in known_paths:
1490 # patch is almost as big as the file; don't bother patching
1491 # or a patch + rename cannot take place due to the target
1492 # directory not existing
1493 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001494 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001495 if sf.name in renames:
1496 del renames[sf.name]
1497 AddToKnownPaths(tf.name, known_paths)
1498 else:
1499 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1500 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1501 largest_source_size = max(largest_source_size, sf.size)
1502
1503 self.largest_source_size = largest_source_size
1504
1505 def EmitVerification(self, script):
1506 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001507 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001508 if tf.name != sf.name:
1509 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1510 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1511 so_far += sf.size
1512 return so_far
1513
Michael Runge63f01de2014-10-28 19:24:19 -07001514 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001515 for fn, _, sha1 in self.verbatim_targets:
1516 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001517 script.FileCheck("/"+fn, sha1)
1518 for tf, _, _, _ in self.patch_list:
1519 script.FileCheck(tf.name, tf.sha1)
1520
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001521 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001522 file_list = ["/" + i[0] for i in self.verbatim_targets]
1523 file_list += ["/" + i for i in self.source_data
1524 if i not in self.target_data and i not in self.renames]
1525 file_list += list(extras)
1526 # Sort the list in descending order, which removes all the files first
1527 # before attempting to remove the folder. (Bug: 22960996)
1528 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001529
1530 def TotalPatchSize(self):
1531 return sum(i[1].size for i in self.patch_list)
1532
1533 def EmitPatches(self, script, total_patch_size, so_far):
1534 self.deferred_patch_list = deferred_patch_list = []
1535 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001536 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001537 if tf.name == "system/build.prop":
1538 deferred_patch_list.append(item)
1539 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001540 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001541 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001542 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1543 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001544 so_far += tf.size
1545 script.SetProgress(so_far / total_patch_size)
1546 return so_far
1547
1548 def EmitDeferredPatches(self, script):
1549 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001550 tf, sf, _, _ = item
1551 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1552 "patch/" + sf.name + ".p")
1553 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001554
1555 def EmitRenames(self, script):
1556 if len(self.renames) > 0:
1557 script.Print("Renaming files...")
1558 for src, tgt in self.renames.iteritems():
1559 print "Renaming " + src + " to " + tgt.name
1560 script.RenameFile(src, tgt.name)
1561
1562
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001563def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001564 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1565 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1566
Doug Zongker26e66192014-02-20 13:22:07 -08001567 if (OPTIONS.block_based and
1568 target_has_recovery_patch and
1569 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001570 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1571
Doug Zongker37974732010-09-16 17:44:38 -07001572 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1573 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001574
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001575 if source_version == 0:
1576 print ("WARNING: generating edify script for a source that "
1577 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001578 script = edify_generator.EdifyGenerator(
1579 source_version, OPTIONS.target_info_dict,
1580 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001581
Tao Bao34b47bf2015-06-22 19:17:41 -07001582 recovery_mount_options = OPTIONS.source_info_dict.get(
1583 "recovery_mount_options")
Tao Bao7fc951c2016-03-15 13:20:19 -07001584 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1585 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001586 oem_dicts = None
Tao Bao7fc951c2016-03-15 13:20:19 -07001587 if source_oem_props or target_oem_props:
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001588 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -07001589
Dan Albert8b72aef2015-03-23 19:13:21 -07001590 metadata = {
Tao Bao7fc951c2016-03-15 13:20:19 -07001591 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001592 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001593 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001594 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001595 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001596
Tao Bao99c17252017-02-07 11:21:17 -08001597 HandleDowngradeMetadata(metadata)
Tao Bao4da324e2016-02-23 11:38:39 -08001598
Doug Zongker05d3dea2009-06-22 11:32:31 -07001599 device_specific = common.DeviceSpecificParams(
1600 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001601 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001602 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001603 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001604 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001605 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001606 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001607 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001608
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001609 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001610 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001611 if HasVendorPartition(target_zip):
1612 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001613 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001614 else:
1615 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001616
Tao Bao7fc951c2016-03-15 13:20:19 -07001617 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001618 OPTIONS.target_info_dict)
Tao Bao7fc951c2016-03-15 13:20:19 -07001619 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001620 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001621
Tao Bao7fc951c2016-03-15 13:20:19 -07001622 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001623 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao7fc951c2016-03-15 13:20:19 -07001624 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001625 script.AssertSomeThumbprint(
1626 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1627 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao7fc951c2016-03-15 13:20:19 -07001628 elif source_oem_props is None and target_oem_props is not None:
1629 script.AssertFingerprintOrThumbprint(
1630 source_fp,
1631 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1632 else:
1633 script.AssertFingerprintOrThumbprint(
1634 target_fp,
1635 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001636
Doug Zongker2ea21062010-04-28 16:05:21 -07001637 metadata["pre-build"] = source_fp
1638 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001639 metadata["pre-build-incremental"] = GetBuildProp(
1640 "ro.build.version.incremental", OPTIONS.source_info_dict)
1641 metadata["post-build-incremental"] = GetBuildProp(
1642 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001643
Doug Zongker55d93282011-01-25 17:03:34 -08001644 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001645 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1646 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001647 target_boot = common.GetBootableImage(
1648 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001649 updating_boot = (not OPTIONS.two_step and
1650 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001651
Doug Zongker55d93282011-01-25 17:03:34 -08001652 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001653 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1654 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001655 target_recovery = common.GetBootableImage(
1656 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001657 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001658
Doug Zongker881dd402009-09-20 14:03:55 -07001659 # Here's how we divide up the progress bar:
1660 # 0.1 for verifying the start state (PatchCheck calls)
1661 # 0.8 for applying patches (ApplyPatch calls)
1662 # 0.1 for unpacking verbatim files, symlinking, and doing the
1663 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001664
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08001665 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001666 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001667
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001668 # Two-step incremental package strategy (in chronological order,
1669 # which is *not* the order in which the generated script has
1670 # things):
1671 #
1672 # if stage is not "2/3" or "3/3":
1673 # do verification on current system
1674 # write recovery image to boot partition
1675 # set stage to "2/3"
1676 # reboot to boot partition and restart recovery
1677 # else if stage is "2/3":
1678 # write recovery image to recovery partition
1679 # set stage to "3/3"
1680 # reboot to recovery partition and restart recovery
1681 # else:
1682 # (stage must be "3/3")
1683 # perform update:
1684 # patch system files, etc.
1685 # force full install of new boot image
1686 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001687 # complete script normally
1688 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001689
1690 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001691 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001692 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001693 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001694 assert fs.fs_type.upper() == "EMMC", \
1695 "two-step packages only supported on devices with EMMC /misc partitions"
1696 bcb_dev = {"bcb_dev": fs.device}
1697 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1698 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001699if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001700""" % bcb_dev)
Tao Bao47ec5ab2016-11-30 12:11:57 -08001701
1702 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1703 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001704 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001705 script.WriteRawImage("/recovery", "recovery.img")
1706 script.AppendExtra("""
1707set_stage("%(bcb_dev)s", "3/3");
1708reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001709else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001710""" % bcb_dev)
1711
Tao Bao47ec5ab2016-11-30 12:11:57 -08001712 # Stage 1/3: (a) Verify the current system.
1713 script.Comment("Stage 1/3")
1714
Tao Bao6c55a8a2015-04-08 15:30:27 -07001715 # Dump fingerprints
1716 script.Print("Source: %s" % (source_fp,))
1717 script.Print("Target: %s" % (target_fp,))
1718
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001719 script.Print("Verifying current system...")
1720
Doug Zongkere5ff5902012-01-17 10:55:37 -08001721 device_specific.IncrementalOTA_VerifyBegin()
1722
Doug Zongker881dd402009-09-20 14:03:55 -07001723 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001724 so_far = system_diff.EmitVerification(script)
1725 if vendor_diff:
1726 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001727
Tao Baob4cfca52016-02-04 14:26:02 -08001728 size = []
1729 if system_diff.patch_list:
1730 size.append(system_diff.largest_source_size)
1731 if vendor_diff:
1732 if vendor_diff.patch_list:
1733 size.append(vendor_diff.largest_source_size)
1734
Doug Zongker5da317e2009-06-02 13:38:17 -07001735 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001736 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001737 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001738 print "boot target: %d source: %d diff: %d" % (
1739 target_boot.size, source_boot.size, len(d))
1740
Doug Zongker048e7ca2009-06-15 14:31:53 -07001741 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001742
Tao Baodd24da92015-07-29 14:09:23 -07001743 boot_type, boot_device = common.GetTypeAndDevice(
1744 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001745
1746 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1747 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001748 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001749 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001750 so_far += source_boot.size
Tao Baob4cfca52016-02-04 14:26:02 -08001751 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001752
Tao Baob4cfca52016-02-04 14:26:02 -08001753 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001754 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001755
Doug Zongker05d3dea2009-06-22 11:32:31 -07001756 device_specific.IncrementalOTA_VerifyEnd()
1757
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001758 if OPTIONS.two_step:
Tao Bao47ec5ab2016-11-30 12:11:57 -08001759 # Stage 1/3: (b) Write recovery image to /boot.
1760 _WriteRecoveryImageToBoot(script, output_zip)
1761
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001762 script.AppendExtra("""
1763set_stage("%(bcb_dev)s", "2/3");
1764reboot_now("%(bcb_dev)s", "");
1765else
1766""" % bcb_dev)
1767
Tao Bao47ec5ab2016-11-30 12:11:57 -08001768 # Stage 3/3: Make changes.
1769 script.Comment("Stage 3/3")
1770
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001771 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001772
Doug Zongkere5ff5902012-01-17 10:55:37 -08001773 device_specific.IncrementalOTA_InstallBegin()
1774
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001775 if OPTIONS.two_step:
1776 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1777 script.WriteRawImage("/boot", "boot.img")
1778 print "writing full boot image (forced by two-step mode)"
1779
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001780 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001781 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1782 if vendor_diff:
1783 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001784
Doug Zongker881dd402009-09-20 14:03:55 -07001785 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001786 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1787 if vendor_diff:
1788 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001789 if updating_boot:
1790 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001791
1792 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001793 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1794 if vendor_diff:
1795 script.Print("Patching vendor files...")
1796 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001797
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001798 if not OPTIONS.two_step:
1799 if updating_boot:
1800 # Produce the boot image by applying a patch to the current
1801 # contents of the boot partition, and write it back to the
1802 # partition.
1803 script.Print("Patching boot image...")
1804 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1805 % (boot_type, boot_device,
1806 source_boot.size, source_boot.sha1,
1807 target_boot.size, target_boot.sha1),
1808 "-",
1809 target_boot.size, target_boot.sha1,
1810 source_boot.sha1, "patch/boot.img.p")
1811 so_far += target_boot.size
1812 script.SetProgress(so_far / total_patch_size)
1813 print "boot image changed; including."
1814 else:
1815 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001816
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001817 system_items = ItemSet("system", "META/filesystem_config.txt")
1818 if vendor_diff:
1819 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1820
Doug Zongkereef39442009-04-02 12:14:19 -07001821 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001822 # Recovery is generated as a patch using both the boot image
1823 # (which contains the same linux kernel as recovery) and the file
1824 # /system/etc/recovery-resource.dat (which contains all the images
1825 # used in the recovery UI) as sources. This lets us minimize the
1826 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001827 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001828 # For older builds where recovery-resource.dat is not present, we
1829 # use only the boot image as the source.
1830
Doug Zongkerc9253822014-02-04 12:17:58 -08001831 if not target_has_recovery_patch:
1832 def output_sink(fn, data):
1833 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001834 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001835
1836 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1837 target_recovery, target_boot)
1838 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001839 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001840 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001841 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001842 else:
1843 print "recovery image unchanged; skipping."
1844
Doug Zongker881dd402009-09-20 14:03:55 -07001845 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001846
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001847 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1848 if vendor_diff:
1849 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1850
1851 temp_script = script.MakeTemporary()
1852 system_items.GetMetadata(target_zip)
1853 system_items.Get("system").SetPermissions(temp_script)
1854 if vendor_diff:
1855 vendor_items.GetMetadata(target_zip)
1856 vendor_items.Get("vendor").SetPermissions(temp_script)
1857
1858 # Note that this call will mess up the trees of Items, so make sure
1859 # we're done with them.
1860 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1861 if vendor_diff:
1862 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001863
1864 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001865 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1866
1867 # Delete all the symlinks in source that aren't in target. This
1868 # needs to happen before verbatim files are unpacked, in case a
1869 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001870
1871 # If a symlink in the source will be replaced by a regular file, we cannot
1872 # delete the symlink/file in case the package gets applied again. For such
1873 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1874 # (Bug: 23646151)
1875 replaced_symlinks = dict()
1876 if system_diff:
1877 for i in system_diff.verbatim_targets:
1878 replaced_symlinks["/%s" % (i[0],)] = i[2]
1879 if vendor_diff:
1880 for i in vendor_diff.verbatim_targets:
1881 replaced_symlinks["/%s" % (i[0],)] = i[2]
1882
1883 if system_diff:
1884 for tf in system_diff.renames.values():
1885 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1886 if vendor_diff:
1887 for tf in vendor_diff.renames.values():
1888 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1889
1890 always_delete = []
1891 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001892 for dest, link in source_symlinks:
1893 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001894 if link in replaced_symlinks:
1895 may_delete.append((link, replaced_symlinks[link]))
1896 else:
1897 always_delete.append(link)
1898 script.DeleteFiles(always_delete)
1899 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001900
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001901 if system_diff.verbatim_targets:
1902 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001903 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001904 if vendor_diff and vendor_diff.verbatim_targets:
1905 script.Print("Unpacking new vendor files...")
1906 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001907
Doug Zongkerc9253822014-02-04 12:17:58 -08001908 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001909 script.Print("Unpacking new recovery...")
1910 script.UnpackPackageDir("recovery", "/system")
1911
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001912 system_diff.EmitRenames(script)
1913 if vendor_diff:
1914 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001915
Doug Zongker05d3dea2009-06-22 11:32:31 -07001916 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001917
1918 # Create all the symlinks that don't already exist, or point to
1919 # somewhere different than what we want. Delete each symlink before
1920 # creating it, since the 'symlink' command won't overwrite.
1921 to_create = []
1922 for dest, link in target_symlinks:
1923 if link in source_symlinks_d:
1924 if dest != source_symlinks_d[link]:
1925 to_create.append((dest, link))
1926 else:
1927 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001928 script.DeleteFiles([i[1] for i in to_create])
1929 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001930
1931 # Now that the symlinks are created, we can set all the
1932 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001933 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001934
Doug Zongker881dd402009-09-20 14:03:55 -07001935 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001936 device_specific.IncrementalOTA_InstallEnd()
1937
Doug Zongker1c390a22009-05-14 19:06:36 -07001938 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001939 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001940
Doug Zongkere92f15a2011-08-26 13:46:40 -07001941 # Patch the build.prop file last, so if something fails but the
1942 # device can still come up, it appears to be the old build and will
1943 # get set the OTA package again to retry.
1944 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001945 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001946
Doug Zongker922206e2014-03-04 13:16:24 -08001947 if OPTIONS.wipe_user_data:
1948 script.Print("Erasing user data...")
1949 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001950 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001951
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001952 if OPTIONS.two_step:
1953 script.AppendExtra("""
1954set_stage("%(bcb_dev)s", "");
1955endif;
1956endif;
1957""" % bcb_dev)
1958
Michael Runge63f01de2014-10-28 19:24:19 -07001959 if OPTIONS.verify and system_diff:
1960 script.Print("Remounting and verifying system partition files...")
1961 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001962 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001963 system_diff.EmitExplicitTargetVerification(script)
1964
1965 if OPTIONS.verify and vendor_diff:
1966 script.Print("Remounting and verifying vendor partition files...")
1967 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001968 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001969 vendor_diff.EmitExplicitTargetVerification(script)
Tao Baofa41fb22016-03-08 17:53:39 -08001970
1971 # For downgrade OTAs, we prefer to use the update-binary in the source
1972 # build that is actually newer than the one in the target build.
1973 if OPTIONS.downgrade:
1974 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1975 else:
1976 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001977
Tao Baob4cfca52016-02-04 14:26:02 -08001978 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001979 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001980
1981
1982def main(argv):
1983
1984 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001985 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001986 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001987 elif o in ("-k", "--package_key"):
1988 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001989 elif o in ("-i", "--incremental_from"):
1990 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001991 elif o == "--full_radio":
1992 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001993 elif o == "--full_bootloader":
1994 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001995 elif o in ("-w", "--wipe_user_data"):
1996 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001997 elif o in ("-n", "--no_prereq"):
1998 OPTIONS.omit_prereq = True
Tao Bao4da324e2016-02-23 11:38:39 -08001999 elif o == "--downgrade":
2000 OPTIONS.downgrade = True
2001 OPTIONS.wipe_user_data = True
Tao Bao9f884e62017-02-28 11:48:48 -08002002 elif o == "--override_timestamp":
2003 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07002004 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh1c50b812017-02-16 13:06:55 -08002005 OPTIONS.oem_source = a.split(',')
Tao Baodf4cb0b2016-02-25 19:49:55 -08002006 elif o == "--oem_no_mount":
2007 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002008 elif o in ("-e", "--extra_script"):
2009 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07002010 elif o in ("-a", "--aslr_mode"):
2011 if a in ("on", "On", "true", "True", "yes", "Yes"):
2012 OPTIONS.aslr_mode = True
2013 else:
2014 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002015 elif o in ("-t", "--worker_threads"):
2016 if a.isdigit():
2017 OPTIONS.worker_threads = int(a)
2018 else:
2019 raise ValueError("Cannot parse value %r for option %r - only "
2020 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002021 elif o in ("-2", "--two_step"):
2022 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002023 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002024 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002025 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002026 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002027 elif o == "--block":
2028 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002029 elif o in ("-b", "--binary"):
2030 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002031 elif o in ("--no_fallback_to_full",):
2032 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002033 elif o == "--stash_threshold":
2034 try:
2035 OPTIONS.stash_threshold = float(a)
2036 except ValueError:
2037 raise ValueError("Cannot parse value %r for option %r - expecting "
2038 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002039 elif o == "--gen_verify":
2040 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002041 elif o == "--log_diff":
2042 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002043 elif o == "--payload_signer":
2044 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002045 elif o == "--payload_signer_args":
2046 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002047 else:
2048 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002049 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002050
2051 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07002052 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002053 extra_long_opts=[
2054 "board_config=",
2055 "package_key=",
2056 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002057 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002058 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002059 "wipe_user_data",
2060 "no_prereq",
Tao Bao4da324e2016-02-23 11:38:39 -08002061 "downgrade",
Tao Bao9f884e62017-02-28 11:48:48 -08002062 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002063 "extra_script=",
2064 "worker_threads=",
2065 "aslr_mode=",
2066 "two_step",
2067 "no_signing",
2068 "block",
2069 "binary=",
2070 "oem_settings=",
Tao Baodf4cb0b2016-02-25 19:49:55 -08002071 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002072 "verify",
2073 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002074 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002075 "gen_verify",
2076 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002077 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002078 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002079 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002080
2081 if len(args) != 2:
2082 common.Usage(__doc__)
2083 sys.exit(1)
2084
Tao Bao4da324e2016-02-23 11:38:39 -08002085 if OPTIONS.downgrade:
2086 # Sanity check to enforce a data wipe.
2087 if not OPTIONS.wipe_user_data:
2088 raise ValueError("Cannot downgrade without a data wipe")
2089
2090 # We should only allow downgrading incrementals (as opposed to full).
2091 # Otherwise the device may go back from arbitrary build with this full
2092 # OTA package.
2093 if OPTIONS.incremental_source is None:
2094 raise ValueError("Cannot generate downgradable full OTAs - consider"
2095 "using --omit_prereq?")
2096
Tao Bao9f884e62017-02-28 11:48:48 -08002097 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
2098 "Cannot have --downgrade AND --override_timestamp both"
2099
Tao Baoc098e9e2016-01-07 13:03:56 -08002100 # Load the dict file from the zip directly to have a peek at the OTA type.
2101 # For packages using A/B update, unzipping is not needed.
2102 input_zip = zipfile.ZipFile(args[0], "r")
2103 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2104 common.ZipClose(input_zip)
2105
2106 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2107
2108 if ab_update:
2109 if OPTIONS.incremental_source is not None:
2110 OPTIONS.target_info_dict = OPTIONS.info_dict
2111 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2112 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2113 common.ZipClose(source_zip)
2114
2115 if OPTIONS.verbose:
2116 print "--- target info ---"
2117 common.DumpInfoDict(OPTIONS.info_dict)
2118
2119 if OPTIONS.incremental_source is not None:
2120 print "--- source info ---"
2121 common.DumpInfoDict(OPTIONS.source_info_dict)
2122
2123 WriteABOTAPackageWithBrilloScript(
2124 target_file=args[0],
2125 output_file=args[1],
2126 source_file=OPTIONS.incremental_source)
2127
2128 print "done."
2129 return
2130
Doug Zongker1c390a22009-05-14 19:06:36 -07002131 if OPTIONS.extra_script is not None:
2132 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2133
Doug Zongkereef39442009-04-02 12:14:19 -07002134 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08002135 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002136
Doug Zongkereef39442009-04-02 12:14:19 -07002137 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002138 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002139
Doug Zongker37974732010-09-16 17:44:38 -07002140 if OPTIONS.verbose:
2141 print "--- target info ---"
2142 common.DumpInfoDict(OPTIONS.info_dict)
2143
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002144 # If the caller explicitly specified the device-specific extensions
2145 # path via -s/--device_specific, use that. Otherwise, use
2146 # META/releasetools.py if it is present in the target target_files.
2147 # Otherwise, take the path of the file from 'tool_extensions' in the
2148 # info dict and look for that in the local filesystem, relative to
2149 # the current directory.
2150
Doug Zongker37974732010-09-16 17:44:38 -07002151 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002152 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2153 if os.path.exists(from_input):
2154 print "(using device-specific extensions from target_files)"
2155 OPTIONS.device_specific = from_input
2156 else:
2157 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2158
Doug Zongker37974732010-09-16 17:44:38 -07002159 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002160 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002161
Tao Baoc098e9e2016-01-07 13:03:56 -08002162 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002163 raise common.ExternalError(
2164 "--- target build has specified no recovery ---")
2165
Tao Bao767e3ac2015-11-10 12:19:19 -08002166 # Use the default key to sign the package if not specified with package_key.
2167 if not OPTIONS.no_signing:
2168 if OPTIONS.package_key is None:
2169 OPTIONS.package_key = OPTIONS.info_dict.get(
2170 "default_system_dev_certificate",
2171 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002172
Tao Bao767e3ac2015-11-10 12:19:19 -08002173 # Set up the output zip. Create a temporary zip file if signing is needed.
2174 if OPTIONS.no_signing:
2175 if os.path.exists(args[1]):
2176 os.unlink(args[1])
2177 output_zip = zipfile.ZipFile(args[1], "w",
2178 compression=zipfile.ZIP_DEFLATED)
2179 else:
2180 temp_zip_file = tempfile.NamedTemporaryFile()
2181 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2182 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002183
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002184 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002185 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002186 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002187 print "--- can't determine the cache partition size ---"
2188 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002189
Tao Bao9bc6bb22015-11-09 16:58:28 -08002190 # Generate a verify package.
2191 if OPTIONS.gen_verify:
2192 WriteVerifyPackage(input_zip, output_zip)
2193
Tao Bao767e3ac2015-11-10 12:19:19 -08002194 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002195 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002196 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002197
2198 # Generate an incremental OTA. It will fall back to generate a full OTA on
2199 # failure unless no_fallback_to_full is specified.
2200 else:
2201 print "unzipping source target-files..."
2202 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2203 OPTIONS.incremental_source)
2204 OPTIONS.target_info_dict = OPTIONS.info_dict
2205 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2206 OPTIONS.source_tmp)
2207 if OPTIONS.verbose:
2208 print "--- source info ---"
2209 common.DumpInfoDict(OPTIONS.source_info_dict)
2210 try:
2211 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002212 if OPTIONS.log_diff:
2213 out_file = open(OPTIONS.log_diff, 'w')
2214 import target_files_diff
2215 target_files_diff.recursiveDiff('',
2216 OPTIONS.source_tmp,
2217 OPTIONS.input_tmp,
2218 out_file)
2219 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002220 except ValueError:
2221 if not OPTIONS.fallback_to_full:
2222 raise
2223 print "--- failed to build incremental; falling back to full ---"
2224 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002225 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002226
Tao Bao767e3ac2015-11-10 12:19:19 -08002227 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002228
Tao Bao767e3ac2015-11-10 12:19:19 -08002229 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002230 if not OPTIONS.no_signing:
2231 SignOutput(temp_zip_file.name, args[1])
2232 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002233
Doug Zongkereef39442009-04-02 12:14:19 -07002234 print "done."
2235
2236
2237if __name__ == '__main__':
2238 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002239 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002240 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002241 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002242 print
2243 print " ERROR: %s" % (e,)
2244 print
2245 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002246 finally:
2247 common.Cleanup()