blob: d409d94f9927356323726af6e2eca408bd4c617f [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given a target-files zipfile, produces an OTA package that installs
19that build. An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage: ota_from_target_files [flags] input_target_files output_ota_package
23
Doug Zongker25568482014-03-03 10:21:27 -080024 --board_config <file>
Doug Zongkerfdd8e692009-08-03 17:27:48 -070025 Deprecated.
Doug Zongkereef39442009-04-02 12:14:19 -070026
Doug Zongkerafb32ea2011-09-22 10:28:04 -070027 -k (--package_key) <key> Key to use to sign the package (default is
28 the value of default_system_dev_certificate from the input
29 target-files's META/misc_info.txt, or
30 "build/target/product/security/testkey" if that value is not
31 specified).
32
33 For incremental OTAs, the default value is based on the source
34 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070035
36 -i (--incremental_from) <file>
37 Generate an incremental OTA using the given target-files zip as
38 the starting build.
39
Tao Bao43078aa2015-04-21 14:32:35 -070040 --full_radio
41 When generating an incremental OTA, always include a full copy of
42 radio image. This option is only meaningful when -i is specified,
43 because a full radio is always included in a full OTA if applicable.
44
leozwangaa6c1a12015-08-14 10:57:58 -070045 --full_bootloader
46 Similar to --full_radio. When generating an incremental OTA, always
47 include a full copy of bootloader image.
48
Michael Runge63f01de2014-10-28 19:24:19 -070049 -v (--verify)
50 Remount and verify the checksums of the files written to the
51 system and vendor (if used) partitions. Incremental builds only.
52
Michael Runge6e836112014-04-15 17:40:21 -070053 -o (--oem_settings) <file>
54 Use the file to specify the expected OEM-specific properties
55 on the OEM partition of the intended device.
56
Tao Baodf4cb0b2016-02-25 19:49:55 -080057 --oem_no_mount
58 For devices with OEM-specific properties but without an OEM partition,
59 do not mount the OEM partition in the updater-script. This should be
60 very rarely used, since it's expected to have a dedicated OEM partition
61 for OEM-specific properties. Only meaningful when -o is specified.
62
Doug Zongkerdbfaae52009-04-21 17:12:54 -070063 -w (--wipe_user_data)
64 Generate an OTA package that will wipe the user data partition
65 when installed.
66
Doug Zongker962069c2009-04-23 11:41:58 -070067 -n (--no_prereq)
68 Omit the timestamp prereq check normally included at the top of
69 the build scripts (used for developer OTA packages which
70 legitimately need to go back and forth).
71
Tao Bao4da324e2016-02-23 11:38:39 -080072 --downgrade
73 Intentionally generate an incremental OTA that updates from a newer
74 build to an older one (based on timestamp comparison). "post-timestamp"
75 will be replaced by "ota-downgrade=yes" in the metadata file. A data
76 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Baofa41fb22016-03-08 17:53:39 -080077 the metadata file. The update-binary in the source build will be used in
78 the OTA package, unless --binary flag is specified.
Tao Bao4da324e2016-02-23 11:38:39 -080079
Doug Zongker1c390a22009-05-14 19:06:36 -070080 -e (--extra_script) <file>
81 Insert the contents of file at the end of the update script.
82
Hristo Bojinovdafb0422010-08-26 14:35:16 -070083 -a (--aslr_mode) <on|off>
84 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050085
Doug Zongker9b23f2c2013-11-25 14:44:12 -080086 -2 (--two_step)
87 Generate a 'two-step' OTA package, where recovery is updated
88 first, so that any changes made to the system partition are done
89 using the new recovery (new kernel, etc.).
90
Doug Zongker26e66192014-02-20 13:22:07 -080091 --block
92 Generate a block-based OTA if possible. Will fall back to a
93 file-based OTA if the target_files is older and doesn't support
94 block-based OTAs.
95
Doug Zongker25568482014-03-03 10:21:27 -080096 -b (--binary) <file>
97 Use the given binary as the update-binary in the output package,
98 instead of the binary in the build's target_files. Use for
99 development only.
100
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200101 -t (--worker_threads) <int>
102 Specifies the number of worker-threads that will be used when
103 generating patches for incremental updates (defaults to 3).
104
Tao Bao8dcf7382015-05-21 14:09:49 -0700105 --stash_threshold <float>
106 Specifies the threshold that will be used to compute the maximum
107 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800108
109 --gen_verify
110 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800111
112 --log_diff <file>
113 Generate a log file that shows the differences in the source and target
114 builds for an incremental package. This option is only meaningful when
115 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700116
117 --payload_signer <signer>
118 Specify the signer when signing the payload and metadata for A/B OTAs.
119 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
120 with the package private key. If the private key cannot be accessed
121 directly, a payload signer that knows how to do that should be specified.
122 The signer will be supplied with "-inkey <path_to_key>",
123 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700124
125 --payload_signer_args <args>
126 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700127"""
128
129import sys
130
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800131if sys.hexversion < 0x02070000:
132 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -0700133 sys.exit(1)
134
Doug Zongkerfc44a512014-08-26 13:10:25 -0700135import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700136import os
Tao Baoc098e9e2016-01-07 13:03:56 -0800137import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700138import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700139import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700140import zipfile
141
142import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700143import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700144import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700145
146OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700147OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700148OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700149OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700150OPTIONS.require_verbatim = set()
151OPTIONS.prohibit_verbatim = set(("system/build.prop",))
152OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700153OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700154OPTIONS.omit_prereq = False
Tao Bao4da324e2016-02-23 11:38:39 -0800155OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700156OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700157OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700158OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
159if OPTIONS.worker_threads == 0:
160 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800161OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900162OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800163OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800164OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700165OPTIONS.oem_source = None
Tao Baodf4cb0b2016-02-25 19:49:55 -0800166OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700167OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700168OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700169OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700170# Stash size cannot exceed cache_size * threshold.
171OPTIONS.cache_size = None
172OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800173OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800174OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700175OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700176OPTIONS.payload_signer_args = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700177
Doug Zongkereef39442009-04-02 12:14:19 -0700178def MostPopularKey(d, default):
179 """Given a dict, return the key corresponding to the largest
180 value. Returns 'default' if the dict is empty."""
181 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700182 if not x:
183 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700184 x.sort()
185 return x[-1][1]
186
187
188def IsSymlink(info):
189 """Return true if the zipfile.ZipInfo object passed in represents a
190 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700191 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700192
Hristo Bojinov96be7202010-08-02 10:26:17 -0700193def IsRegular(info):
194 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700195 regular file."""
196 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700197
Michael Runge4038aa82013-12-13 18:06:28 -0800198def ClosestFileMatch(src, tgtfiles, existing):
199 """Returns the closest file match between a source file and list
200 of potential matches. The exact filename match is preferred,
201 then the sha1 is searched for, and finally a file with the same
202 basename is evaluated. Rename support in the updater-binary is
203 required for the latter checks to be used."""
204
205 result = tgtfiles.get("path:" + src.name)
206 if result is not None:
207 return result
208
209 if not OPTIONS.target_info_dict.get("update_rename_support", False):
210 return None
211
212 if src.size < 1000:
213 return None
214
215 result = tgtfiles.get("sha1:" + src.sha1)
216 if result is not None and existing.get(result.name) is None:
217 return result
218 result = tgtfiles.get("file:" + src.name.split("/")[-1])
219 if result is not None and existing.get(result.name) is None:
220 return result
221 return None
222
Dan Albert8b72aef2015-03-23 19:13:21 -0700223class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700224 def __init__(self, partition, fs_config):
225 self.partition = partition
226 self.fs_config = fs_config
227 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700228
Dan Albert8b72aef2015-03-23 19:13:21 -0700229 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700230 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700231 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700232 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700233
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700234 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700235 # The target_files contains a record of what the uid,
236 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700237 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700238
239 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700240 if not line:
241 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700242 columns = line.split()
243 name, uid, gid, mode = columns[:4]
244 selabel = None
245 capabilities = None
246
247 # After the first 4 columns, there are a series of key=value
248 # pairs. Extract out the fields we care about.
249 for element in columns[4:]:
250 key, value = element.split("=")
251 if key == "selabel":
252 selabel = value
253 if key == "capabilities":
254 capabilities = value
255
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700256 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700257 if i is not None:
258 i.uid = int(uid)
259 i.gid = int(gid)
260 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700261 i.selabel = selabel
262 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700263 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700264 i.children.sort(key=lambda i: i.name)
265
Tao Baof2cffbd2015-07-22 12:33:18 -0700266 # Set metadata for the files generated by this script. For full recovery
267 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700268 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700269 if i:
270 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700271 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700272 if i:
273 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700274
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700275
Dan Albert8b72aef2015-03-23 19:13:21 -0700276class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700277 """Items represent the metadata (user, group, mode) of files and
278 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700279 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700280 self.itemset = itemset
281 self.name = name
282 self.uid = None
283 self.gid = None
284 self.mode = None
285 self.selabel = None
286 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700287 self.is_dir = is_dir
288 self.descendants = None
289 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700290
291 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700292 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700293 self.parent.children.append(self)
294 else:
295 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700296 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700297 self.children = []
298
299 def Dump(self, indent=0):
300 if self.uid is not None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700301 print "%s%s %d %d %o" % (
302 " " * indent, self.name, self.uid, self.gid, self.mode)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700303 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700304 print "%s%s %s %s %s" % (
305 " " * indent, self.name, self.uid, self.gid, self.mode)
306 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700307 print "%s%s" % (" "*indent, self.descendants)
308 print "%s%s" % (" "*indent, self.best_subtree)
309 for i in self.children:
310 i.Dump(indent=indent+1)
311
Doug Zongkereef39442009-04-02 12:14:19 -0700312 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700313 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700314 all children and determine the best strategy for using set_perm_recursive
315 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700316 values. Recursively calls itself for all descendants.
317
Dan Albert8b72aef2015-03-23 19:13:21 -0700318 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
319 counting up all descendants of this node. (dmode or fmode may be None.)
320 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
321 fmode, selabel, capabilities) tuple that will match the most descendants of
322 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700323 """
324
Dan Albert8b72aef2015-03-23 19:13:21 -0700325 assert self.is_dir
326 key = (self.uid, self.gid, self.mode, None, self.selabel,
327 self.capabilities)
328 self.descendants = {key: 1}
329 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700330 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700331 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700332 for k, v in i.CountChildMetadata().iteritems():
333 d[k] = d.get(k, 0) + v
334 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700335 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700336 d[k] = d.get(k, 0) + 1
337
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700338 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
339 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700340
341 # First, find the (uid, gid) pair that matches the most
342 # descendants.
343 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700344 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700345 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
346 ug = MostPopularKey(ug, (0, 0))
347
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700348 # Now find the dmode, fmode, selabel, and capabilities that match
349 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700350 best_dmode = (0, 0o755)
351 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700352 best_selabel = (0, None)
353 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700354 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700355 if k[:2] != ug:
356 continue
357 if k[2] is not None and count >= best_dmode[0]:
358 best_dmode = (count, k[2])
359 if k[3] is not None and count >= best_fmode[0]:
360 best_fmode = (count, k[3])
361 if k[4] is not None and count >= best_selabel[0]:
362 best_selabel = (count, k[4])
363 if k[5] is not None and count >= best_capabilities[0]:
364 best_capabilities = (count, k[5])
365 self.best_subtree = ug + (
366 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700367
368 return d
369
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700370 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700371 """Append set_perm/set_perm_recursive commands to 'script' to
372 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700373 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700374
375 self.CountChildMetadata()
376
377 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700378 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
379 # that the current item (and all its children) have already been set to.
380 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700381 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700382 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700383 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700384 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700385 current = item.best_subtree
386
387 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700388 item.mode != current[2] or item.selabel != current[4] or \
389 item.capabilities != current[5]:
390 script.SetPermissions("/"+item.name, item.uid, item.gid,
391 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700392
393 for i in item.children:
394 recurse(i, current)
395 else:
396 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700397 item.mode != current[3] or item.selabel != current[4] or \
398 item.capabilities != current[5]:
399 script.SetPermissions("/"+item.name, item.uid, item.gid,
400 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700401
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700402 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700403
404
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700405def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
406 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700407 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800408 list of symlinks. output_zip may be None, in which case the copy is
409 skipped (but the other side effects still happen). substitute is an
410 optional dict of {output filename: contents} to be output instead of
411 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700412 """
413
414 symlinks = []
415
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700416 partition = itemset.partition
417
Doug Zongkereef39442009-04-02 12:14:19 -0700418 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700419 prefix = partition.upper() + "/"
420 if info.filename.startswith(prefix):
421 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700422 if IsSymlink(info):
423 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700424 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700425 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700426 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700427 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700428 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700429 if substitute and fn in substitute and substitute[fn] is None:
430 continue
431 if output_zip is not None:
432 if substitute and fn in substitute:
433 data = substitute[fn]
434 else:
435 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700436 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700437 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700438 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700439 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700440 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700441
442 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800443 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700444
445
Doug Zongkereef39442009-04-02 12:14:19 -0700446def SignOutput(temp_zip_name, output_zip_name):
447 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
448 pw = key_passwords[OPTIONS.package_key]
449
Doug Zongker951495f2009-08-14 12:44:19 -0700450 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
451 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700452
453
Dan Albert8b72aef2015-03-23 19:13:21 -0700454def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700455 oem_props = info_dict.get("oem_fingerprint_properties")
Michael Runge560569a2014-09-18 15:12:45 -0700456 if oem_props is None or len(oem_props) == 0:
Michael Runge6e836112014-04-15 17:40:21 -0700457 device = GetBuildProp("ro.product.device", info_dict)
458 script.AssertDevice(device)
459 else:
460 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700461 raise common.ExternalError(
462 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700463 for prop in oem_props.split():
464 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700465 raise common.ExternalError(
466 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700467 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700468
Doug Zongkereef39442009-04-02 12:14:19 -0700469
Tao Bao47ec5ab2016-11-30 12:11:57 -0800470def _WriteRecoveryImageToBoot(script, output_zip):
471 """Find and write recovery image to /boot in two-step OTA.
472
473 In two-step OTAs, we write recovery image to /boot as the first step so that
474 we can reboot to there and install a new recovery image to /recovery.
475 A special "recovery-two-step.img" will be preferred, which encodes the correct
476 path of "/boot". Otherwise the device may show "device is corrupt" message
477 when booting into /boot.
478
479 Fall back to using the regular recovery.img if the two-step recovery image
480 doesn't exist. Note that rebuilding the special image at this point may be
481 infeasible, because we don't have the desired boot signer and keys when
482 calling ota_from_target_files.py.
483 """
484
485 recovery_two_step_img_name = "recovery-two-step.img"
486 recovery_two_step_img_path = os.path.join(
487 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
488 if os.path.exists(recovery_two_step_img_path):
489 recovery_two_step_img = common.GetBootableImage(
490 recovery_two_step_img_name, recovery_two_step_img_name,
491 OPTIONS.input_tmp, "RECOVERY")
492 common.ZipWriteStr(
493 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
494 print "two-step package: using %s in stage 1/3" % (
495 recovery_two_step_img_name,)
496 script.WriteRawImage("/boot", recovery_two_step_img_name)
497 else:
498 print "two-step package: using recovery.img in stage 1/3"
499 # The "recovery.img" entry has been written into package earlier.
500 script.WriteRawImage("/boot", "recovery.img")
501
502
Doug Zongkerc9253822014-02-04 12:17:58 -0800503def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700504 namelist = [name for name in target_files_zip.namelist()]
505 return ("SYSTEM/recovery-from-boot.p" in namelist or
506 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700507
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700508def HasVendorPartition(target_files_zip):
509 try:
510 target_files_zip.getinfo("VENDOR/")
511 return True
512 except KeyError:
513 return False
514
Michael Runge6e836112014-04-15 17:40:21 -0700515def GetOemProperty(name, oem_props, oem_dict, info_dict):
516 if oem_props is not None and name in oem_props:
517 return oem_dict[name]
518 return GetBuildProp(name, info_dict)
519
520
521def CalculateFingerprint(oem_props, oem_dict, info_dict):
522 if oem_props is None:
523 return GetBuildProp("ro.build.fingerprint", info_dict)
524 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700525 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
526 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
527 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
528 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700529
Doug Zongkerfc44a512014-08-26 13:10:25 -0700530
Doug Zongker3c84f562014-07-31 11:06:30 -0700531def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700532 # Return an image object (suitable for passing to BlockImageDiff)
533 # for the 'which' partition (most be "system" or "vendor"). If a
534 # prebuilt image and file map are found in tmpdir they are used,
535 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700536
537 assert which in ("system", "vendor")
538
539 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700540 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
541 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700542 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700543 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700544
545 else:
546 print "building %s.img from target-files" % (which,)
547
548 # This is an 'old' target-files, which does not contain images
549 # already built. Build them.
550
Doug Zongkerfc44a512014-08-26 13:10:25 -0700551 mappath = tempfile.mkstemp()[1]
552 OPTIONS.tempfiles.append(mappath)
553
Doug Zongker3c84f562014-07-31 11:06:30 -0700554 import add_img_to_target_files
555 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700556 path = add_img_to_target_files.BuildSystem(
557 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700558 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700559 path = add_img_to_target_files.BuildVendor(
560 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700561
Tao Baoff777812015-05-12 11:42:31 -0700562 # Bug: http://b/20939131
563 # In ext4 filesystems, block 0 might be changed even being mounted
564 # R/O. We add it to clobbered_blocks so that it will be written to the
565 # target unconditionally. Note that they are still part of care_map.
566 clobbered_blocks = "0"
567
568 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700569
570
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700571def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700572 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700573 # be installed on top of. For now, we expect the API just won't
574 # change very often. Similarly for fstab, it might have changed
575 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700576 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700577
Michael Runge6e836112014-04-15 17:40:21 -0700578 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700579 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -0700580 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -0700581 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -0700582 if OPTIONS.oem_source is None:
583 raise common.ExternalError("OEM source required for this build")
Tao Baodf4cb0b2016-02-25 19:49:55 -0800584 if not OPTIONS.oem_no_mount:
585 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700586 oem_dict = common.LoadDictionaryFromLines(
587 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700588
Dan Albert8b72aef2015-03-23 19:13:21 -0700589 metadata = {
590 "post-build": CalculateFingerprint(oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700591 OPTIONS.info_dict),
Dan Albert8b72aef2015-03-23 19:13:21 -0700592 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
593 OPTIONS.info_dict),
594 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
595 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700596
Doug Zongker05d3dea2009-06-22 11:32:31 -0700597 device_specific = common.DeviceSpecificParams(
598 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700599 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700600 output_zip=output_zip,
601 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700602 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700603 metadata=metadata,
604 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700605
Doug Zongkerc9253822014-02-04 12:17:58 -0800606 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800607 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800608
Tao Baob4cfca52016-02-04 14:26:02 -0800609 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
610
Doug Zongker962069c2009-04-23 11:41:58 -0700611 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700612 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700613 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
614 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700615
Michael Runge6e836112014-04-15 17:40:21 -0700616 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700617 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800618
619 # Two-step package strategy (in chronological order, which is *not*
620 # the order in which the generated script has things):
621 #
622 # if stage is not "2/3" or "3/3":
623 # write recovery image to boot partition
624 # set stage to "2/3"
625 # reboot to boot partition and restart recovery
626 # else if stage is "2/3":
627 # write recovery image to recovery partition
628 # set stage to "3/3"
629 # reboot to recovery partition and restart recovery
630 # else:
631 # (stage must be "3/3")
632 # set stage to ""
633 # do normal full package installation:
634 # wipe and install system, boot image, etc.
635 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700636 # complete script normally
637 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800638
639 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
640 OPTIONS.input_tmp, "RECOVERY")
641 if OPTIONS.two_step:
642 if not OPTIONS.info_dict.get("multistage_support", None):
643 assert False, "two-step packages not supported by this build"
644 fs = OPTIONS.info_dict["fstab"]["/misc"]
645 assert fs.fs_type.upper() == "EMMC", \
646 "two-step packages only supported on devices with EMMC /misc partitions"
647 bcb_dev = {"bcb_dev": fs.device}
648 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
649 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700650if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800651""" % bcb_dev)
Tao Bao47ec5ab2016-11-30 12:11:57 -0800652
653 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
654 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800655 script.WriteRawImage("/recovery", "recovery.img")
656 script.AppendExtra("""
657set_stage("%(bcb_dev)s", "3/3");
658reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700659else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800660""" % bcb_dev)
661
Tao Bao47ec5ab2016-11-30 12:11:57 -0800662 # Stage 3/3: Make changes.
663 script.Comment("Stage 3/3")
664
Tao Bao6c55a8a2015-04-08 15:30:27 -0700665 # Dump fingerprints
666 script.Print("Target: %s" % CalculateFingerprint(
667 oem_props, oem_dict, OPTIONS.info_dict))
668
Doug Zongkere5ff5902012-01-17 10:55:37 -0800669 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700670
Doug Zongker01ce19c2014-02-04 13:48:15 -0800671 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700672
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700673 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800674 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700675 if HasVendorPartition(input_zip):
676 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700677
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400678 # Place a copy of file_contexts.bin into the OTA package which will be used
679 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700680 if "selinux_fc" in OPTIONS.info_dict:
681 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500682
Michael Runge7cd99ba2014-10-22 17:21:48 -0700683 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
684
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700685 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700686 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800687
Doug Zongker26e66192014-02-20 13:22:07 -0800688 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700689 # Full OTA is done as an "incremental" against an empty source
690 # image. This has the effect of writing new data from the package
691 # to the entire partition, but lets us reuse the updater code that
692 # writes incrementals to do it.
693 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
694 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700695 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700696 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800697 else:
698 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700699 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800700 if not has_recovery_patch:
701 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800702 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700703
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700704 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800705 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700706
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700707 boot_img = common.GetBootableImage(
708 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800709
Doug Zongker91a99c22014-05-09 13:15:01 -0700710 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800711 def output_sink(fn, data):
712 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700713 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800714
715 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
716 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700717
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700718 system_items.GetMetadata(input_zip)
719 system_items.Get("system").SetPermissions(script)
720
721 if HasVendorPartition(input_zip):
722 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
723 script.ShowProgress(0.1, 0)
724
725 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700726 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
727 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700728 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700729 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700730 else:
731 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700732 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700733 script.UnpackPackageDir("vendor", "/vendor")
734
735 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
736 script.MakeSymlinks(symlinks)
737
738 vendor_items.GetMetadata(input_zip)
739 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700740
Doug Zongker37974732010-09-16 17:44:38 -0700741 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700742 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700743
Doug Zongker01ce19c2014-02-04 13:48:15 -0800744 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700745 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700746
Doug Zongker01ce19c2014-02-04 13:48:15 -0800747 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700748 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700749
Doug Zongker1c390a22009-05-14 19:06:36 -0700750 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700751 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700752
Doug Zongker14833602010-02-02 13:12:04 -0800753 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800754
Doug Zongker922206e2014-03-04 13:16:24 -0800755 if OPTIONS.wipe_user_data:
756 script.ShowProgress(0.1, 10)
757 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700758
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800759 if OPTIONS.two_step:
760 script.AppendExtra("""
761set_stage("%(bcb_dev)s", "");
762""" % bcb_dev)
763 script.AppendExtra("else\n")
Tao Bao47ec5ab2016-11-30 12:11:57 -0800764
765 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
766 script.Comment("Stage 1/3")
767 _WriteRecoveryImageToBoot(script, output_zip)
768
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800769 script.AppendExtra("""
770set_stage("%(bcb_dev)s", "2/3");
771reboot_now("%(bcb_dev)s", "");
772endif;
773endif;
774""" % bcb_dev)
Tao Baob4cfca52016-02-04 14:26:02 -0800775
Tao Bao4da324e2016-02-23 11:38:39 -0800776 script.SetProgress(1)
777 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -0800778 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700779 WriteMetadata(metadata, output_zip)
780
Doug Zongkerfc44a512014-08-26 13:10:25 -0700781
Dan Albert8e0178d2015-01-27 15:53:15 -0800782def WritePolicyConfig(file_name, output_zip):
783 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500784
Doug Zongker2ea21062010-04-28 16:05:21 -0700785
786def WriteMetadata(metadata, output_zip):
787 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
788 "".join(["%s=%s\n" % kv
789 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700790
Doug Zongkerfc44a512014-08-26 13:10:25 -0700791
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700792def LoadPartitionFiles(z, partition):
793 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700794 ZipFile, and return a dict of {filename: File object}."""
795 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700796 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700797 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700798 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700799 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700800 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700801 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700802 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800803 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700804
805
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700806def GetBuildProp(prop, info_dict):
807 """Return the fingerprint of the build of a given target-files info_dict."""
808 try:
809 return info_dict.get("build.prop", {})[prop]
810 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700811 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700812
Doug Zongkerfc44a512014-08-26 13:10:25 -0700813
Michael Runge4038aa82013-12-13 18:06:28 -0800814def AddToKnownPaths(filename, known_paths):
815 if filename[-1] == "/":
816 return
817 dirs = filename.split("/")[:-1]
818 while len(dirs) > 0:
819 path = "/".join(dirs)
820 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700821 break
Michael Runge4038aa82013-12-13 18:06:28 -0800822 known_paths.add(path)
823 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700824
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700825
Geremy Condra36bd3652014-02-06 19:45:10 -0800826def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700827 # TODO(tbao): We should factor out the common parts between
828 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800829 source_version = OPTIONS.source_info_dict["recovery_api_version"]
830 target_version = OPTIONS.target_info_dict["recovery_api_version"]
831
832 if source_version == 0:
833 print ("WARNING: generating edify script for a source that "
834 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700835 script = edify_generator.EdifyGenerator(
836 source_version, OPTIONS.target_info_dict,
837 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800838
Tao Bao3806c232015-07-05 21:08:33 -0700839 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
840 recovery_mount_options = OPTIONS.source_info_dict.get(
841 "recovery_mount_options")
842 oem_dict = None
843 if oem_props is not None and len(oem_props) > 0:
844 if OPTIONS.oem_source is None:
845 raise common.ExternalError("OEM source required for this build")
Tao Baodf4cb0b2016-02-25 19:49:55 -0800846 if not OPTIONS.oem_no_mount:
847 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700848 oem_dict = common.LoadDictionaryFromLines(
849 open(OPTIONS.oem_source).readlines())
850
Dan Albert8b72aef2015-03-23 19:13:21 -0700851 metadata = {
Tao Bao3806c232015-07-05 21:08:33 -0700852 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
853 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -0800854 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700855 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800856
Tao Bao4da324e2016-02-23 11:38:39 -0800857 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
858 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
859 is_downgrade = long(post_timestamp) < long(pre_timestamp)
860
861 if OPTIONS.downgrade:
862 metadata["ota-downgrade"] = "yes"
863 if not is_downgrade:
864 raise RuntimeError("--downgrade specified but no downgrade detected: "
865 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
866 else:
867 if is_downgrade:
868 # Non-fatal here to allow generating such a package which may require
869 # manual work to adjust the post-timestamp. A legit use case is that we
870 # cut a new build C (after having A and B), but want to enfore the
871 # update path of A -> C -> B. Specifying --downgrade may not help since
872 # that would enforce a data wipe for C -> B update.
873 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
874 "The package may not be deployed properly. "
875 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
876 metadata["post-timestamp"] = post_timestamp
877
Geremy Condra36bd3652014-02-06 19:45:10 -0800878 device_specific = common.DeviceSpecificParams(
879 source_zip=source_zip,
880 source_version=source_version,
881 target_zip=target_zip,
882 target_version=target_version,
883 output_zip=output_zip,
884 script=script,
885 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700886 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800887
Tao Bao3806c232015-07-05 21:08:33 -0700888 source_fp = CalculateFingerprint(oem_props, oem_dict,
889 OPTIONS.source_info_dict)
890 target_fp = CalculateFingerprint(oem_props, oem_dict,
891 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800892 metadata["pre-build"] = source_fp
893 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700894 metadata["pre-build-incremental"] = GetBuildProp(
895 "ro.build.version.incremental", OPTIONS.source_info_dict)
896 metadata["post-build-incremental"] = GetBuildProp(
897 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800898
899 source_boot = common.GetBootableImage(
900 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
901 OPTIONS.source_info_dict)
902 target_boot = common.GetBootableImage(
903 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
904 updating_boot = (not OPTIONS.two_step and
905 (source_boot.data != target_boot.data))
906
Geremy Condra36bd3652014-02-06 19:45:10 -0800907 target_recovery = common.GetBootableImage(
908 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800909
Doug Zongkerfc44a512014-08-26 13:10:25 -0700910 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
911 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700912
913 blockimgdiff_version = 1
914 if OPTIONS.info_dict:
915 blockimgdiff_version = max(
916 int(i) for i in
917 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
918
Tao Baof8acad12016-07-07 09:09:58 -0700919 # Check the first block of the source system partition for remount R/W only
920 # if the filesystem is ext4.
921 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
922 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700923 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
924 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
925 # b) the blocks listed in block map may not contain all the bytes for a given
926 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700927 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
928 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
929 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700930 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800931 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700932 version=blockimgdiff_version,
933 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700934
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700935 if HasVendorPartition(target_zip):
936 if not HasVendorPartition(source_zip):
937 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700938 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
939 OPTIONS.source_info_dict)
940 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
941 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800942
943 # Check first block of vendor partition for remount R/W only if
944 # disk type is ext4
945 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baob4cfca52016-02-04 14:26:02 -0800946 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700947 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700948 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800949 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700950 version=blockimgdiff_version,
951 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700952 else:
953 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800954
Michael Rungec6e3afd2014-05-05 11:55:47 -0700955 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800956 device_specific.IncrementalOTA_Assertions()
957
958 # Two-step incremental package strategy (in chronological order,
959 # which is *not* the order in which the generated script has
960 # things):
961 #
962 # if stage is not "2/3" or "3/3":
963 # do verification on current system
964 # write recovery image to boot partition
965 # set stage to "2/3"
966 # reboot to boot partition and restart recovery
967 # else if stage is "2/3":
968 # write recovery image to recovery partition
969 # set stage to "3/3"
970 # reboot to recovery partition and restart recovery
971 # else:
972 # (stage must be "3/3")
973 # perform update:
974 # patch system files, etc.
975 # force full install of new boot image
976 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700977 # complete script normally
978 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800979
980 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700981 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800982 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700983 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800984 assert fs.fs_type.upper() == "EMMC", \
985 "two-step packages only supported on devices with EMMC /misc partitions"
986 bcb_dev = {"bcb_dev": fs.device}
987 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
988 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700989if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800990""" % bcb_dev)
Tao Bao47ec5ab2016-11-30 12:11:57 -0800991
992 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
993 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700994 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800995 script.WriteRawImage("/recovery", "recovery.img")
996 script.AppendExtra("""
997set_stage("%(bcb_dev)s", "3/3");
998reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700999else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001000""" % bcb_dev)
1001
Tao Bao47ec5ab2016-11-30 12:11:57 -08001002 # Stage 1/3: (a) Verify the current system.
1003 script.Comment("Stage 1/3")
1004
Tao Bao6c55a8a2015-04-08 15:30:27 -07001005 # Dump fingerprints
1006 script.Print("Source: %s" % CalculateFingerprint(
1007 oem_props, oem_dict, OPTIONS.source_info_dict))
1008 script.Print("Target: %s" % CalculateFingerprint(
1009 oem_props, oem_dict, OPTIONS.target_info_dict))
1010
Geremy Condra36bd3652014-02-06 19:45:10 -08001011 script.Print("Verifying current system...")
1012
1013 device_specific.IncrementalOTA_VerifyBegin()
1014
Michael Rungec6e3afd2014-05-05 11:55:47 -07001015 if oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001016 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1017 # patching on a device that's already on the target build will damage the
1018 # system. Because operations like move don't check the block state, they
1019 # always apply the changes unconditionally.
1020 if blockimgdiff_version <= 2:
1021 script.AssertSomeFingerprint(source_fp)
1022 else:
1023 script.AssertSomeFingerprint(source_fp, target_fp)
Michael Rungec6e3afd2014-05-05 11:55:47 -07001024 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001025 if blockimgdiff_version <= 2:
1026 script.AssertSomeThumbprint(
1027 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1028 else:
1029 script.AssertSomeThumbprint(
1030 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1031 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001032
Tao Baob4cfca52016-02-04 14:26:02 -08001033 # Check the required cache size (i.e. stashed blocks).
1034 size = []
1035 if system_diff:
1036 size.append(system_diff.required_cache)
1037 if vendor_diff:
1038 size.append(vendor_diff.required_cache)
1039
Geremy Condra36bd3652014-02-06 19:45:10 -08001040 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001041 boot_type, boot_device = common.GetTypeAndDevice(
1042 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001043 d = common.Difference(target_boot, source_boot)
1044 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001045 if d is None:
1046 include_full_boot = True
1047 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1048 else:
1049 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001050
Doug Zongkerf8340082014-08-05 10:39:37 -07001051 print "boot target: %d source: %d diff: %d" % (
1052 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001053
Doug Zongkerf8340082014-08-05 10:39:37 -07001054 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001055
Doug Zongkerf8340082014-08-05 10:39:37 -07001056 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1057 (boot_type, boot_device,
1058 source_boot.size, source_boot.sha1,
1059 target_boot.size, target_boot.sha1))
Tao Baob4cfca52016-02-04 14:26:02 -08001060 size.append(target_boot.size)
1061
1062 if size:
1063 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001064
1065 device_specific.IncrementalOTA_VerifyEnd()
1066
1067 if OPTIONS.two_step:
Tao Bao47ec5ab2016-11-30 12:11:57 -08001068 # Stage 1/3: (b) Write recovery image to /boot.
1069 _WriteRecoveryImageToBoot(script, output_zip)
1070
Geremy Condra36bd3652014-02-06 19:45:10 -08001071 script.AppendExtra("""
1072set_stage("%(bcb_dev)s", "2/3");
1073reboot_now("%(bcb_dev)s", "");
1074else
1075""" % bcb_dev)
1076
Tao Bao47ec5ab2016-11-30 12:11:57 -08001077 # Stage 3/3: Make changes.
1078 script.Comment("Stage 3/3")
1079
Jesse Zhao75bcea02015-01-06 10:59:53 -08001080 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001081 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001082 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001083 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001084
Geremy Condra36bd3652014-02-06 19:45:10 -08001085 script.Comment("---- start making changes here ----")
1086
1087 device_specific.IncrementalOTA_InstallBegin()
1088
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001089 system_diff.WriteScript(script, output_zip,
1090 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001091
Doug Zongkerfc44a512014-08-26 13:10:25 -07001092 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001093 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001094
1095 if OPTIONS.two_step:
1096 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1097 script.WriteRawImage("/boot", "boot.img")
1098 print "writing full boot image (forced by two-step mode)"
1099
1100 if not OPTIONS.two_step:
1101 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001102 if include_full_boot:
1103 print "boot image changed; including full."
1104 script.Print("Installing boot image...")
1105 script.WriteRawImage("/boot", "boot.img")
1106 else:
1107 # Produce the boot image by applying a patch to the current
1108 # contents of the boot partition, and write it back to the
1109 # partition.
1110 print "boot image changed; including patch."
1111 script.Print("Patching boot image...")
1112 script.ShowProgress(0.1, 10)
1113 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1114 % (boot_type, boot_device,
1115 source_boot.size, source_boot.sha1,
1116 target_boot.size, target_boot.sha1),
1117 "-",
1118 target_boot.size, target_boot.sha1,
1119 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001120 else:
1121 print "boot image unchanged; skipping."
1122
1123 # Do device-specific installation (eg, write radio image).
1124 device_specific.IncrementalOTA_InstallEnd()
1125
1126 if OPTIONS.extra_script is not None:
1127 script.AppendExtra(OPTIONS.extra_script)
1128
Doug Zongker922206e2014-03-04 13:16:24 -08001129 if OPTIONS.wipe_user_data:
1130 script.Print("Erasing user data...")
1131 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001132 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001133
Geremy Condra36bd3652014-02-06 19:45:10 -08001134 if OPTIONS.two_step:
1135 script.AppendExtra("""
1136set_stage("%(bcb_dev)s", "");
1137endif;
1138endif;
1139""" % bcb_dev)
1140
1141 script.SetProgress(1)
Tao Baofa41fb22016-03-08 17:53:39 -08001142 # For downgrade OTAs, we prefer to use the update-binary in the source
1143 # build that is actually newer than the one in the target build.
1144 if OPTIONS.downgrade:
1145 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1146 else:
1147 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001148 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001149 WriteMetadata(metadata, output_zip)
1150
Doug Zongker32b527d2014-03-04 10:03:02 -08001151
Tao Bao9bc6bb22015-11-09 16:58:28 -08001152def WriteVerifyPackage(input_zip, output_zip):
1153 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1154
1155 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1156 recovery_mount_options = OPTIONS.info_dict.get(
1157 "recovery_mount_options")
1158 oem_dict = None
1159 if oem_props is not None and len(oem_props) > 0:
1160 if OPTIONS.oem_source is None:
1161 raise common.ExternalError("OEM source required for this build")
1162 script.Mount("/oem", recovery_mount_options)
1163 oem_dict = common.LoadDictionaryFromLines(
1164 open(OPTIONS.oem_source).readlines())
1165
1166 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1167 metadata = {
1168 "post-build": target_fp,
1169 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1170 OPTIONS.info_dict),
1171 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1172 }
1173
1174 device_specific = common.DeviceSpecificParams(
1175 input_zip=input_zip,
1176 input_version=OPTIONS.info_dict["recovery_api_version"],
1177 output_zip=output_zip,
1178 script=script,
1179 input_tmp=OPTIONS.input_tmp,
1180 metadata=metadata,
1181 info_dict=OPTIONS.info_dict)
1182
1183 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1184
1185 script.Print("Verifying device images against %s..." % target_fp)
1186 script.AppendExtra("")
1187
1188 script.Print("Verifying boot...")
1189 boot_img = common.GetBootableImage(
1190 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1191 boot_type, boot_device = common.GetTypeAndDevice(
1192 "/boot", OPTIONS.info_dict)
1193 script.Verify("%s:%s:%d:%s" % (
1194 boot_type, boot_device, boot_img.size, boot_img.sha1))
1195 script.AppendExtra("")
1196
1197 script.Print("Verifying recovery...")
1198 recovery_img = common.GetBootableImage(
1199 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1200 recovery_type, recovery_device = common.GetTypeAndDevice(
1201 "/recovery", OPTIONS.info_dict)
1202 script.Verify("%s:%s:%d:%s" % (
1203 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1204 script.AppendExtra("")
1205
1206 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1207 system_tgt.ResetFileMap()
1208 system_diff = common.BlockDifference("system", system_tgt, src=None)
1209 system_diff.WriteStrictVerifyScript(script)
1210
1211 if HasVendorPartition(input_zip):
1212 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1213 vendor_tgt.ResetFileMap()
1214 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1215 vendor_diff.WriteStrictVerifyScript(script)
1216
1217 # Device specific partitions, such as radio, bootloader and etc.
1218 device_specific.VerifyOTA_Assertions()
1219
1220 script.SetProgress(1.0)
1221 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001222 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001223 WriteMetadata(metadata, output_zip)
1224
1225
Tao Baoc098e9e2016-01-07 13:03:56 -08001226def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1227 source_file=None):
1228 """Generate an Android OTA package that has A/B update payload."""
1229
1230 # Setup signing keys.
1231 if OPTIONS.package_key is None:
1232 OPTIONS.package_key = OPTIONS.info_dict.get(
1233 "default_system_dev_certificate",
1234 "build/target/product/security/testkey")
1235
Tao Baodea0f8b2016-06-20 17:55:06 -07001236 # A/B updater expects a signing key in RSA format. Gets the key ready for
1237 # later use in step 3, unless a payload_signer has been specified.
1238 if OPTIONS.payload_signer is None:
1239 cmd = ["openssl", "pkcs8",
1240 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1241 "-inform", "DER", "-nocrypt"]
1242 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1243 cmd.extend(["-out", rsa_key])
1244 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1245 p1.wait()
1246 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001247
Tao Baodea0f8b2016-06-20 17:55:06 -07001248 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001249 temp_zip_file = tempfile.NamedTemporaryFile()
1250 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1251 compression=zipfile.ZIP_DEFLATED)
1252
1253 # Metadata to comply with Android OTA package format.
1254 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1255 oem_dict = None
1256 if oem_props:
1257 if OPTIONS.oem_source is None:
1258 raise common.ExternalError("OEM source required for this build")
1259 oem_dict = common.LoadDictionaryFromLines(
1260 open(OPTIONS.oem_source).readlines())
1261
1262 metadata = {
1263 "post-build": CalculateFingerprint(oem_props, oem_dict,
1264 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001265 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1266 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001267 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1268 OPTIONS.info_dict),
1269 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001270 "ota-required-cache": "0",
1271 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001272 }
1273
1274 if source_file is not None:
1275 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1276 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001277 metadata["pre-build-incremental"] = GetBuildProp(
1278 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001279
1280 # 1. Generate payload.
1281 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1282 cmd = ["brillo_update_payload", "generate",
1283 "--payload", payload_file,
1284 "--target_image", target_file]
1285 if source_file is not None:
1286 cmd.extend(["--source_image", source_file])
1287 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1288 p1.wait()
1289 assert p1.returncode == 0, "brillo_update_payload generate failed"
1290
1291 # 2. Generate hashes of the payload and metadata files.
1292 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1293 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1294 cmd = ["brillo_update_payload", "hash",
1295 "--unsigned_payload", payload_file,
1296 "--signature_size", "256",
1297 "--metadata_hash_file", metadata_sig_file,
1298 "--payload_hash_file", payload_sig_file]
1299 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1300 p1.wait()
1301 assert p1.returncode == 0, "brillo_update_payload hash failed"
1302
1303 # 3. Sign the hashes and insert them back into the payload file.
1304 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1305 suffix=".bin")
1306 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1307 suffix=".bin")
1308 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001309 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001310 cmd = [OPTIONS.payload_signer]
1311 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001312 else:
1313 cmd = ["openssl", "pkeyutl", "-sign",
1314 "-inkey", rsa_key,
1315 "-pkeyopt", "digest:sha256"]
1316 cmd.extend(["-in", payload_sig_file,
1317 "-out", signed_payload_sig_file])
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001318
Tao Baoc098e9e2016-01-07 13:03:56 -08001319 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1320 p1.wait()
1321 assert p1.returncode == 0, "openssl sign payload failed"
1322
1323 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001324 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001325 cmd = [OPTIONS.payload_signer]
1326 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001327 else:
1328 cmd = ["openssl", "pkeyutl", "-sign",
1329 "-inkey", rsa_key,
1330 "-pkeyopt", "digest:sha256"]
1331 cmd.extend(["-in", metadata_sig_file,
1332 "-out", signed_metadata_sig_file])
Tao Baoc098e9e2016-01-07 13:03:56 -08001333 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1334 p1.wait()
1335 assert p1.returncode == 0, "openssl sign metadata failed"
1336
1337 # 3c. Insert the signatures back into the payload file.
1338 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1339 suffix=".bin")
1340 cmd = ["brillo_update_payload", "sign",
1341 "--unsigned_payload", payload_file,
1342 "--payload", signed_payload_file,
1343 "--signature_size", "256",
1344 "--metadata_signature_file", signed_metadata_sig_file,
1345 "--payload_signature_file", signed_payload_sig_file]
1346 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1347 p1.wait()
1348 assert p1.returncode == 0, "brillo_update_payload sign failed"
1349
Alex Deymo19241c12016-02-04 22:29:29 -08001350 # 4. Dump the signed payload properties.
1351 properties_file = common.MakeTempFile(prefix="payload-properties-",
1352 suffix=".txt")
1353 cmd = ["brillo_update_payload", "properties",
1354 "--payload", signed_payload_file,
1355 "--properties_file", properties_file]
1356 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1357 p1.wait()
1358 assert p1.returncode == 0, "brillo_update_payload properties failed"
1359
Tao Bao38ca0be2016-06-14 17:48:11 -07001360 if OPTIONS.wipe_user_data:
1361 with open(properties_file, "a") as f:
1362 f.write("POWERWASH=1\n")
1363 metadata["ota-wipe"] = "yes"
1364
Alex Deymo19241c12016-02-04 22:29:29 -08001365 # Add the signed payload file and properties into the zip.
1366 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001367 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1368 compress_type=zipfile.ZIP_STORED)
1369 WriteMetadata(metadata, output_zip)
1370
Tianjie Xucfa86222016-03-07 16:31:19 -08001371 # If dm-verity is supported for the device, copy contents of care_map
1372 # into A/B OTA package.
1373 if OPTIONS.info_dict.get("verity") == "true":
1374 target_zip = zipfile.ZipFile(target_file, "r")
1375 care_map_path = "META/care_map.txt"
1376 namelist = target_zip.namelist()
1377 if care_map_path in namelist:
1378 care_map_data = target_zip.read(care_map_path)
1379 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data)
1380 else:
1381 print "Warning: cannot find care map file in target_file package"
1382 common.ZipClose(target_zip)
1383
Tao Baoc098e9e2016-01-07 13:03:56 -08001384 # Sign the whole package to comply with the Android OTA package format.
1385 common.ZipClose(output_zip)
1386 SignOutput(temp_zip_file.name, output_file)
1387 temp_zip_file.close()
1388
1389
Dan Albert8b72aef2015-03-23 19:13:21 -07001390class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001391 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001392 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001393 print "Loading target..."
1394 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1395 print "Loading source..."
1396 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1397
1398 self.verbatim_targets = verbatim_targets = []
1399 self.patch_list = patch_list = []
1400 diffs = []
1401 self.renames = renames = {}
1402 known_paths = set()
1403 largest_source_size = 0
1404
1405 matching_file_cache = {}
1406 for fn, sf in source_data.items():
1407 assert fn == sf.name
1408 matching_file_cache["path:" + fn] = sf
1409 if fn in target_data.keys():
1410 AddToKnownPaths(fn, known_paths)
1411 # Only allow eligibility for filename/sha matching
1412 # if there isn't a perfect path match.
1413 if target_data.get(sf.name) is None:
1414 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1415 matching_file_cache["sha:" + sf.sha1] = sf
1416
1417 for fn in sorted(target_data.keys()):
1418 tf = target_data[fn]
1419 assert fn == tf.name
1420 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1421 if sf is not None and sf.name != tf.name:
1422 print "File has moved from " + sf.name + " to " + tf.name
1423 renames[sf.name] = tf
1424
1425 if sf is None or fn in OPTIONS.require_verbatim:
1426 # This file should be included verbatim
1427 if fn in OPTIONS.prohibit_verbatim:
1428 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1429 print "send", fn, "verbatim"
1430 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001431 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001432 if fn in target_data.keys():
1433 AddToKnownPaths(fn, known_paths)
1434 elif tf.sha1 != sf.sha1:
1435 # File is different; consider sending as a patch
1436 diffs.append(common.Difference(tf, sf))
1437 else:
1438 # Target file data identical to source (may still be renamed)
1439 pass
1440
1441 common.ComputeDifferences(diffs)
1442
1443 for diff in diffs:
1444 tf, sf, d = diff.GetPatch()
1445 path = "/".join(tf.name.split("/")[:-1])
1446 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1447 path not in known_paths:
1448 # patch is almost as big as the file; don't bother patching
1449 # or a patch + rename cannot take place due to the target
1450 # directory not existing
1451 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001452 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001453 if sf.name in renames:
1454 del renames[sf.name]
1455 AddToKnownPaths(tf.name, known_paths)
1456 else:
1457 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1458 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1459 largest_source_size = max(largest_source_size, sf.size)
1460
1461 self.largest_source_size = largest_source_size
1462
1463 def EmitVerification(self, script):
1464 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001465 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001466 if tf.name != sf.name:
1467 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1468 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1469 so_far += sf.size
1470 return so_far
1471
Michael Runge63f01de2014-10-28 19:24:19 -07001472 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001473 for fn, _, sha1 in self.verbatim_targets:
1474 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001475 script.FileCheck("/"+fn, sha1)
1476 for tf, _, _, _ in self.patch_list:
1477 script.FileCheck(tf.name, tf.sha1)
1478
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001479 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001480 file_list = ["/" + i[0] for i in self.verbatim_targets]
1481 file_list += ["/" + i for i in self.source_data
1482 if i not in self.target_data and i not in self.renames]
1483 file_list += list(extras)
1484 # Sort the list in descending order, which removes all the files first
1485 # before attempting to remove the folder. (Bug: 22960996)
1486 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001487
1488 def TotalPatchSize(self):
1489 return sum(i[1].size for i in self.patch_list)
1490
1491 def EmitPatches(self, script, total_patch_size, so_far):
1492 self.deferred_patch_list = deferred_patch_list = []
1493 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001494 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001495 if tf.name == "system/build.prop":
1496 deferred_patch_list.append(item)
1497 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001498 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001499 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001500 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1501 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001502 so_far += tf.size
1503 script.SetProgress(so_far / total_patch_size)
1504 return so_far
1505
1506 def EmitDeferredPatches(self, script):
1507 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001508 tf, sf, _, _ = item
1509 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1510 "patch/" + sf.name + ".p")
1511 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001512
1513 def EmitRenames(self, script):
1514 if len(self.renames) > 0:
1515 script.Print("Renaming files...")
1516 for src, tgt in self.renames.iteritems():
1517 print "Renaming " + src + " to " + tgt.name
1518 script.RenameFile(src, tgt.name)
1519
1520
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001521def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001522 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1523 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1524
Doug Zongker26e66192014-02-20 13:22:07 -08001525 if (OPTIONS.block_based and
1526 target_has_recovery_patch and
1527 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001528 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1529
Doug Zongker37974732010-09-16 17:44:38 -07001530 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1531 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001532
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001533 if source_version == 0:
1534 print ("WARNING: generating edify script for a source that "
1535 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001536 script = edify_generator.EdifyGenerator(
1537 source_version, OPTIONS.target_info_dict,
1538 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001539
Michael Runge6e836112014-04-15 17:40:21 -07001540 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Tao Bao34b47bf2015-06-22 19:17:41 -07001541 recovery_mount_options = OPTIONS.source_info_dict.get(
1542 "recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -07001543 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -07001544 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -07001545 if OPTIONS.oem_source is None:
1546 raise common.ExternalError("OEM source required for this build")
Tao Baobd25fcd2016-03-07 21:24:40 -08001547 if not OPTIONS.oem_no_mount:
1548 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001549 oem_dict = common.LoadDictionaryFromLines(
1550 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001551
Dan Albert8b72aef2015-03-23 19:13:21 -07001552 metadata = {
1553 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1554 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001555 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001556 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001557
Tao Bao4da324e2016-02-23 11:38:39 -08001558 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
1559 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1560 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1561
1562 if OPTIONS.downgrade:
1563 metadata["ota-downgrade"] = "yes"
1564 if not is_downgrade:
1565 raise RuntimeError("--downgrade specified but no downgrade detected: "
1566 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
1567 else:
1568 if is_downgrade:
1569 # Non-fatal here to allow generating such a package which may require
1570 # manual work to adjust the post-timestamp. A legit use case is that we
1571 # cut a new build C (after having A and B), but want to enfore the
1572 # update path of A -> C -> B. Specifying --downgrade may not help since
1573 # that would enforce a data wipe for C -> B update.
1574 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
1575 "The package may not be deployed properly. "
1576 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
1577 metadata["post-timestamp"] = post_timestamp
1578
Doug Zongker05d3dea2009-06-22 11:32:31 -07001579 device_specific = common.DeviceSpecificParams(
1580 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001581 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001582 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001583 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001584 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001585 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001586 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001587 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001588
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001589 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001590 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001591 if HasVendorPartition(target_zip):
1592 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001593 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001594 else:
1595 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001596
Dan Albert8b72aef2015-03-23 19:13:21 -07001597 target_fp = CalculateFingerprint(oem_props, oem_dict,
1598 OPTIONS.target_info_dict)
1599 source_fp = CalculateFingerprint(oem_props, oem_dict,
1600 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001601
1602 if oem_props is None:
1603 script.AssertSomeFingerprint(source_fp, target_fp)
1604 else:
1605 script.AssertSomeThumbprint(
1606 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1607 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1608
Doug Zongker2ea21062010-04-28 16:05:21 -07001609 metadata["pre-build"] = source_fp
1610 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001611 metadata["pre-build-incremental"] = GetBuildProp(
1612 "ro.build.version.incremental", OPTIONS.source_info_dict)
1613 metadata["post-build-incremental"] = GetBuildProp(
1614 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001615
Doug Zongker55d93282011-01-25 17:03:34 -08001616 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001617 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1618 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001619 target_boot = common.GetBootableImage(
1620 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001621 updating_boot = (not OPTIONS.two_step and
1622 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001623
Doug Zongker55d93282011-01-25 17:03:34 -08001624 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001625 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1626 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001627 target_recovery = common.GetBootableImage(
1628 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001629 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001630
Doug Zongker881dd402009-09-20 14:03:55 -07001631 # Here's how we divide up the progress bar:
1632 # 0.1 for verifying the start state (PatchCheck calls)
1633 # 0.8 for applying patches (ApplyPatch calls)
1634 # 0.1 for unpacking verbatim files, symlinking, and doing the
1635 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001636
Michael Runge6e836112014-04-15 17:40:21 -07001637 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001638 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001639
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001640 # Two-step incremental package strategy (in chronological order,
1641 # which is *not* the order in which the generated script has
1642 # things):
1643 #
1644 # if stage is not "2/3" or "3/3":
1645 # do verification on current system
1646 # write recovery image to boot partition
1647 # set stage to "2/3"
1648 # reboot to boot partition and restart recovery
1649 # else if stage is "2/3":
1650 # write recovery image to recovery partition
1651 # set stage to "3/3"
1652 # reboot to recovery partition and restart recovery
1653 # else:
1654 # (stage must be "3/3")
1655 # perform update:
1656 # patch system files, etc.
1657 # force full install of new boot image
1658 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001659 # complete script normally
1660 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001661
1662 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001663 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001664 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001665 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001666 assert fs.fs_type.upper() == "EMMC", \
1667 "two-step packages only supported on devices with EMMC /misc partitions"
1668 bcb_dev = {"bcb_dev": fs.device}
1669 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1670 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001671if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001672""" % bcb_dev)
Tao Bao47ec5ab2016-11-30 12:11:57 -08001673
1674 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1675 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001676 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001677 script.WriteRawImage("/recovery", "recovery.img")
1678 script.AppendExtra("""
1679set_stage("%(bcb_dev)s", "3/3");
1680reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001681else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001682""" % bcb_dev)
1683
Tao Bao47ec5ab2016-11-30 12:11:57 -08001684 # Stage 1/3: (a) Verify the current system.
1685 script.Comment("Stage 1/3")
1686
Tao Bao6c55a8a2015-04-08 15:30:27 -07001687 # Dump fingerprints
1688 script.Print("Source: %s" % (source_fp,))
1689 script.Print("Target: %s" % (target_fp,))
1690
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001691 script.Print("Verifying current system...")
1692
Doug Zongkere5ff5902012-01-17 10:55:37 -08001693 device_specific.IncrementalOTA_VerifyBegin()
1694
Doug Zongker881dd402009-09-20 14:03:55 -07001695 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001696 so_far = system_diff.EmitVerification(script)
1697 if vendor_diff:
1698 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001699
Tao Baob4cfca52016-02-04 14:26:02 -08001700 size = []
1701 if system_diff.patch_list:
1702 size.append(system_diff.largest_source_size)
1703 if vendor_diff:
1704 if vendor_diff.patch_list:
1705 size.append(vendor_diff.largest_source_size)
1706
Doug Zongker5da317e2009-06-02 13:38:17 -07001707 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001708 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001709 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001710 print "boot target: %d source: %d diff: %d" % (
1711 target_boot.size, source_boot.size, len(d))
1712
Doug Zongker048e7ca2009-06-15 14:31:53 -07001713 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001714
Tao Baodd24da92015-07-29 14:09:23 -07001715 boot_type, boot_device = common.GetTypeAndDevice(
1716 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001717
1718 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1719 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001720 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001721 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001722 so_far += source_boot.size
Tao Baob4cfca52016-02-04 14:26:02 -08001723 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001724
Tao Baob4cfca52016-02-04 14:26:02 -08001725 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001726 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001727
Doug Zongker05d3dea2009-06-22 11:32:31 -07001728 device_specific.IncrementalOTA_VerifyEnd()
1729
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001730 if OPTIONS.two_step:
Tao Bao47ec5ab2016-11-30 12:11:57 -08001731 # Stage 1/3: (b) Write recovery image to /boot.
1732 _WriteRecoveryImageToBoot(script, output_zip)
1733
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001734 script.AppendExtra("""
1735set_stage("%(bcb_dev)s", "2/3");
1736reboot_now("%(bcb_dev)s", "");
1737else
1738""" % bcb_dev)
1739
Tao Bao47ec5ab2016-11-30 12:11:57 -08001740 # Stage 3/3: Make changes.
1741 script.Comment("Stage 3/3")
1742
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001743 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001744
Doug Zongkere5ff5902012-01-17 10:55:37 -08001745 device_specific.IncrementalOTA_InstallBegin()
1746
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001747 if OPTIONS.two_step:
1748 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1749 script.WriteRawImage("/boot", "boot.img")
1750 print "writing full boot image (forced by two-step mode)"
1751
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001752 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001753 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1754 if vendor_diff:
1755 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001756
Doug Zongker881dd402009-09-20 14:03:55 -07001757 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001758 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1759 if vendor_diff:
1760 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001761 if updating_boot:
1762 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001763
1764 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001765 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1766 if vendor_diff:
1767 script.Print("Patching vendor files...")
1768 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001769
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001770 if not OPTIONS.two_step:
1771 if updating_boot:
1772 # Produce the boot image by applying a patch to the current
1773 # contents of the boot partition, and write it back to the
1774 # partition.
1775 script.Print("Patching boot image...")
1776 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1777 % (boot_type, boot_device,
1778 source_boot.size, source_boot.sha1,
1779 target_boot.size, target_boot.sha1),
1780 "-",
1781 target_boot.size, target_boot.sha1,
1782 source_boot.sha1, "patch/boot.img.p")
1783 so_far += target_boot.size
1784 script.SetProgress(so_far / total_patch_size)
1785 print "boot image changed; including."
1786 else:
1787 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001788
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001789 system_items = ItemSet("system", "META/filesystem_config.txt")
1790 if vendor_diff:
1791 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1792
Doug Zongkereef39442009-04-02 12:14:19 -07001793 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001794 # Recovery is generated as a patch using both the boot image
1795 # (which contains the same linux kernel as recovery) and the file
1796 # /system/etc/recovery-resource.dat (which contains all the images
1797 # used in the recovery UI) as sources. This lets us minimize the
1798 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001799 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001800 # For older builds where recovery-resource.dat is not present, we
1801 # use only the boot image as the source.
1802
Doug Zongkerc9253822014-02-04 12:17:58 -08001803 if not target_has_recovery_patch:
1804 def output_sink(fn, data):
1805 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001806 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001807
1808 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1809 target_recovery, target_boot)
1810 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001811 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001812 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001813 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001814 else:
1815 print "recovery image unchanged; skipping."
1816
Doug Zongker881dd402009-09-20 14:03:55 -07001817 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001818
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001819 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1820 if vendor_diff:
1821 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1822
1823 temp_script = script.MakeTemporary()
1824 system_items.GetMetadata(target_zip)
1825 system_items.Get("system").SetPermissions(temp_script)
1826 if vendor_diff:
1827 vendor_items.GetMetadata(target_zip)
1828 vendor_items.Get("vendor").SetPermissions(temp_script)
1829
1830 # Note that this call will mess up the trees of Items, so make sure
1831 # we're done with them.
1832 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1833 if vendor_diff:
1834 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001835
1836 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001837 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1838
1839 # Delete all the symlinks in source that aren't in target. This
1840 # needs to happen before verbatim files are unpacked, in case a
1841 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001842
1843 # If a symlink in the source will be replaced by a regular file, we cannot
1844 # delete the symlink/file in case the package gets applied again. For such
1845 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1846 # (Bug: 23646151)
1847 replaced_symlinks = dict()
1848 if system_diff:
1849 for i in system_diff.verbatim_targets:
1850 replaced_symlinks["/%s" % (i[0],)] = i[2]
1851 if vendor_diff:
1852 for i in vendor_diff.verbatim_targets:
1853 replaced_symlinks["/%s" % (i[0],)] = i[2]
1854
1855 if system_diff:
1856 for tf in system_diff.renames.values():
1857 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1858 if vendor_diff:
1859 for tf in vendor_diff.renames.values():
1860 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1861
1862 always_delete = []
1863 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001864 for dest, link in source_symlinks:
1865 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001866 if link in replaced_symlinks:
1867 may_delete.append((link, replaced_symlinks[link]))
1868 else:
1869 always_delete.append(link)
1870 script.DeleteFiles(always_delete)
1871 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001872
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001873 if system_diff.verbatim_targets:
1874 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001875 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001876 if vendor_diff and vendor_diff.verbatim_targets:
1877 script.Print("Unpacking new vendor files...")
1878 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001879
Doug Zongkerc9253822014-02-04 12:17:58 -08001880 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001881 script.Print("Unpacking new recovery...")
1882 script.UnpackPackageDir("recovery", "/system")
1883
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001884 system_diff.EmitRenames(script)
1885 if vendor_diff:
1886 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001887
Doug Zongker05d3dea2009-06-22 11:32:31 -07001888 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001889
1890 # Create all the symlinks that don't already exist, or point to
1891 # somewhere different than what we want. Delete each symlink before
1892 # creating it, since the 'symlink' command won't overwrite.
1893 to_create = []
1894 for dest, link in target_symlinks:
1895 if link in source_symlinks_d:
1896 if dest != source_symlinks_d[link]:
1897 to_create.append((dest, link))
1898 else:
1899 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001900 script.DeleteFiles([i[1] for i in to_create])
1901 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001902
1903 # Now that the symlinks are created, we can set all the
1904 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001905 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001906
Doug Zongker881dd402009-09-20 14:03:55 -07001907 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001908 device_specific.IncrementalOTA_InstallEnd()
1909
Doug Zongker1c390a22009-05-14 19:06:36 -07001910 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001911 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001912
Doug Zongkere92f15a2011-08-26 13:46:40 -07001913 # Patch the build.prop file last, so if something fails but the
1914 # device can still come up, it appears to be the old build and will
1915 # get set the OTA package again to retry.
1916 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001917 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001918
Doug Zongker922206e2014-03-04 13:16:24 -08001919 if OPTIONS.wipe_user_data:
1920 script.Print("Erasing user data...")
1921 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001922 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001923
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001924 if OPTIONS.two_step:
1925 script.AppendExtra("""
1926set_stage("%(bcb_dev)s", "");
1927endif;
1928endif;
1929""" % bcb_dev)
1930
Michael Runge63f01de2014-10-28 19:24:19 -07001931 if OPTIONS.verify and system_diff:
1932 script.Print("Remounting and verifying system partition files...")
1933 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001934 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001935 system_diff.EmitExplicitTargetVerification(script)
1936
1937 if OPTIONS.verify and vendor_diff:
1938 script.Print("Remounting and verifying vendor partition files...")
1939 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001940 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001941 vendor_diff.EmitExplicitTargetVerification(script)
Tao Baofa41fb22016-03-08 17:53:39 -08001942
1943 # For downgrade OTAs, we prefer to use the update-binary in the source
1944 # build that is actually newer than the one in the target build.
1945 if OPTIONS.downgrade:
1946 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1947 else:
1948 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001949
Tao Baob4cfca52016-02-04 14:26:02 -08001950 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001951 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001952
1953
1954def main(argv):
1955
1956 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001957 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001958 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001959 elif o in ("-k", "--package_key"):
1960 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001961 elif o in ("-i", "--incremental_from"):
1962 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001963 elif o == "--full_radio":
1964 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001965 elif o == "--full_bootloader":
1966 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001967 elif o in ("-w", "--wipe_user_data"):
1968 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001969 elif o in ("-n", "--no_prereq"):
1970 OPTIONS.omit_prereq = True
Tao Bao4da324e2016-02-23 11:38:39 -08001971 elif o == "--downgrade":
1972 OPTIONS.downgrade = True
1973 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07001974 elif o in ("-o", "--oem_settings"):
1975 OPTIONS.oem_source = a
Tao Baodf4cb0b2016-02-25 19:49:55 -08001976 elif o == "--oem_no_mount":
1977 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001978 elif o in ("-e", "--extra_script"):
1979 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001980 elif o in ("-a", "--aslr_mode"):
1981 if a in ("on", "On", "true", "True", "yes", "Yes"):
1982 OPTIONS.aslr_mode = True
1983 else:
1984 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001985 elif o in ("-t", "--worker_threads"):
1986 if a.isdigit():
1987 OPTIONS.worker_threads = int(a)
1988 else:
1989 raise ValueError("Cannot parse value %r for option %r - only "
1990 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001991 elif o in ("-2", "--two_step"):
1992 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001993 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001994 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001995 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001996 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001997 elif o == "--block":
1998 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001999 elif o in ("-b", "--binary"):
2000 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002001 elif o in ("--no_fallback_to_full",):
2002 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002003 elif o == "--stash_threshold":
2004 try:
2005 OPTIONS.stash_threshold = float(a)
2006 except ValueError:
2007 raise ValueError("Cannot parse value %r for option %r - expecting "
2008 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002009 elif o == "--gen_verify":
2010 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002011 elif o == "--log_diff":
2012 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002013 elif o == "--payload_signer":
2014 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002015 elif o == "--payload_signer_args":
2016 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002017 else:
2018 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002019 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002020
2021 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07002022 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002023 extra_long_opts=[
2024 "board_config=",
2025 "package_key=",
2026 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002027 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002028 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002029 "wipe_user_data",
2030 "no_prereq",
Tao Bao4da324e2016-02-23 11:38:39 -08002031 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07002032 "extra_script=",
2033 "worker_threads=",
2034 "aslr_mode=",
2035 "two_step",
2036 "no_signing",
2037 "block",
2038 "binary=",
2039 "oem_settings=",
Tao Baodf4cb0b2016-02-25 19:49:55 -08002040 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002041 "verify",
2042 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002043 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002044 "gen_verify",
2045 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002046 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002047 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002048 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002049
2050 if len(args) != 2:
2051 common.Usage(__doc__)
2052 sys.exit(1)
2053
Tao Bao4da324e2016-02-23 11:38:39 -08002054 if OPTIONS.downgrade:
2055 # Sanity check to enforce a data wipe.
2056 if not OPTIONS.wipe_user_data:
2057 raise ValueError("Cannot downgrade without a data wipe")
2058
2059 # We should only allow downgrading incrementals (as opposed to full).
2060 # Otherwise the device may go back from arbitrary build with this full
2061 # OTA package.
2062 if OPTIONS.incremental_source is None:
2063 raise ValueError("Cannot generate downgradable full OTAs - consider"
2064 "using --omit_prereq?")
2065
Tao Baoc098e9e2016-01-07 13:03:56 -08002066 # Load the dict file from the zip directly to have a peek at the OTA type.
2067 # For packages using A/B update, unzipping is not needed.
2068 input_zip = zipfile.ZipFile(args[0], "r")
2069 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2070 common.ZipClose(input_zip)
2071
2072 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2073
2074 if ab_update:
2075 if OPTIONS.incremental_source is not None:
2076 OPTIONS.target_info_dict = OPTIONS.info_dict
2077 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2078 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2079 common.ZipClose(source_zip)
2080
2081 if OPTIONS.verbose:
2082 print "--- target info ---"
2083 common.DumpInfoDict(OPTIONS.info_dict)
2084
2085 if OPTIONS.incremental_source is not None:
2086 print "--- source info ---"
2087 common.DumpInfoDict(OPTIONS.source_info_dict)
2088
2089 WriteABOTAPackageWithBrilloScript(
2090 target_file=args[0],
2091 output_file=args[1],
2092 source_file=OPTIONS.incremental_source)
2093
2094 print "done."
2095 return
2096
Doug Zongker1c390a22009-05-14 19:06:36 -07002097 if OPTIONS.extra_script is not None:
2098 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2099
Doug Zongkereef39442009-04-02 12:14:19 -07002100 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08002101 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002102
Doug Zongkereef39442009-04-02 12:14:19 -07002103 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002104 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002105
Doug Zongker37974732010-09-16 17:44:38 -07002106 if OPTIONS.verbose:
2107 print "--- target info ---"
2108 common.DumpInfoDict(OPTIONS.info_dict)
2109
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002110 # If the caller explicitly specified the device-specific extensions
2111 # path via -s/--device_specific, use that. Otherwise, use
2112 # META/releasetools.py if it is present in the target target_files.
2113 # Otherwise, take the path of the file from 'tool_extensions' in the
2114 # info dict and look for that in the local filesystem, relative to
2115 # the current directory.
2116
Doug Zongker37974732010-09-16 17:44:38 -07002117 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002118 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2119 if os.path.exists(from_input):
2120 print "(using device-specific extensions from target_files)"
2121 OPTIONS.device_specific = from_input
2122 else:
2123 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2124
Doug Zongker37974732010-09-16 17:44:38 -07002125 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002126 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002127
Tao Baoc098e9e2016-01-07 13:03:56 -08002128 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002129 raise common.ExternalError(
2130 "--- target build has specified no recovery ---")
2131
Tao Bao767e3ac2015-11-10 12:19:19 -08002132 # Use the default key to sign the package if not specified with package_key.
2133 if not OPTIONS.no_signing:
2134 if OPTIONS.package_key is None:
2135 OPTIONS.package_key = OPTIONS.info_dict.get(
2136 "default_system_dev_certificate",
2137 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002138
Tao Bao767e3ac2015-11-10 12:19:19 -08002139 # Set up the output zip. Create a temporary zip file if signing is needed.
2140 if OPTIONS.no_signing:
2141 if os.path.exists(args[1]):
2142 os.unlink(args[1])
2143 output_zip = zipfile.ZipFile(args[1], "w",
2144 compression=zipfile.ZIP_DEFLATED)
2145 else:
2146 temp_zip_file = tempfile.NamedTemporaryFile()
2147 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2148 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002149
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002150 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002151 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002152 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002153 print "--- can't determine the cache partition size ---"
2154 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002155
Tao Bao9bc6bb22015-11-09 16:58:28 -08002156 # Generate a verify package.
2157 if OPTIONS.gen_verify:
2158 WriteVerifyPackage(input_zip, output_zip)
2159
Tao Bao767e3ac2015-11-10 12:19:19 -08002160 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002161 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002162 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002163
2164 # Generate an incremental OTA. It will fall back to generate a full OTA on
2165 # failure unless no_fallback_to_full is specified.
2166 else:
2167 print "unzipping source target-files..."
2168 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2169 OPTIONS.incremental_source)
2170 OPTIONS.target_info_dict = OPTIONS.info_dict
2171 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2172 OPTIONS.source_tmp)
2173 if OPTIONS.verbose:
2174 print "--- source info ---"
2175 common.DumpInfoDict(OPTIONS.source_info_dict)
2176 try:
2177 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002178 if OPTIONS.log_diff:
2179 out_file = open(OPTIONS.log_diff, 'w')
2180 import target_files_diff
2181 target_files_diff.recursiveDiff('',
2182 OPTIONS.source_tmp,
2183 OPTIONS.input_tmp,
2184 out_file)
2185 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002186 except ValueError:
2187 if not OPTIONS.fallback_to_full:
2188 raise
2189 print "--- failed to build incremental; falling back to full ---"
2190 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002191 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002192
Tao Bao767e3ac2015-11-10 12:19:19 -08002193 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002194
Tao Bao767e3ac2015-11-10 12:19:19 -08002195 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002196 if not OPTIONS.no_signing:
2197 SignOutput(temp_zip_file.name, args[1])
2198 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002199
Doug Zongkereef39442009-04-02 12:14:19 -07002200 print "done."
2201
2202
2203if __name__ == '__main__':
2204 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002205 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002206 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002207 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002208 print
2209 print " ERROR: %s" % (e,)
2210 print
2211 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002212 finally:
2213 common.Cleanup()