blob: d3d4974ff7bfed80866eb883bbfa01732895536c [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
Doug Zongkerc9253822014-02-04 12:17:58 -0800470def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700471 namelist = [name for name in target_files_zip.namelist()]
472 return ("SYSTEM/recovery-from-boot.p" in namelist or
473 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700474
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700475def HasVendorPartition(target_files_zip):
476 try:
477 target_files_zip.getinfo("VENDOR/")
478 return True
479 except KeyError:
480 return False
481
Michael Runge6e836112014-04-15 17:40:21 -0700482def GetOemProperty(name, oem_props, oem_dict, info_dict):
483 if oem_props is not None and name in oem_props:
484 return oem_dict[name]
485 return GetBuildProp(name, info_dict)
486
487
488def CalculateFingerprint(oem_props, oem_dict, info_dict):
489 if oem_props is None:
490 return GetBuildProp("ro.build.fingerprint", info_dict)
491 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700492 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
493 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
494 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
495 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700496
Doug Zongkerfc44a512014-08-26 13:10:25 -0700497
Doug Zongker3c84f562014-07-31 11:06:30 -0700498def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700499 # Return an image object (suitable for passing to BlockImageDiff)
500 # for the 'which' partition (most be "system" or "vendor"). If a
501 # prebuilt image and file map are found in tmpdir they are used,
502 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700503
504 assert which in ("system", "vendor")
505
506 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700507 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
508 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700509 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700510 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700511
512 else:
513 print "building %s.img from target-files" % (which,)
514
515 # This is an 'old' target-files, which does not contain images
516 # already built. Build them.
517
Doug Zongkerfc44a512014-08-26 13:10:25 -0700518 mappath = tempfile.mkstemp()[1]
519 OPTIONS.tempfiles.append(mappath)
520
Doug Zongker3c84f562014-07-31 11:06:30 -0700521 import add_img_to_target_files
522 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700523 path = add_img_to_target_files.BuildSystem(
524 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700525 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700526 path = add_img_to_target_files.BuildVendor(
527 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700528
Tao Baoff777812015-05-12 11:42:31 -0700529 # Bug: http://b/20939131
530 # In ext4 filesystems, block 0 might be changed even being mounted
531 # R/O. We add it to clobbered_blocks so that it will be written to the
532 # target unconditionally. Note that they are still part of care_map.
533 clobbered_blocks = "0"
534
535 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700536
537
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700538def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700539 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700540 # be installed on top of. For now, we expect the API just won't
541 # change very often. Similarly for fstab, it might have changed
542 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700543 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700544
Michael Runge6e836112014-04-15 17:40:21 -0700545 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700546 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -0700547 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -0700548 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -0700549 if OPTIONS.oem_source is None:
550 raise common.ExternalError("OEM source required for this build")
Tao Baodf4cb0b2016-02-25 19:49:55 -0800551 if not OPTIONS.oem_no_mount:
552 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700553 oem_dict = common.LoadDictionaryFromLines(
554 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700555
Dan Albert8b72aef2015-03-23 19:13:21 -0700556 metadata = {
557 "post-build": CalculateFingerprint(oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700558 OPTIONS.info_dict),
Dan Albert8b72aef2015-03-23 19:13:21 -0700559 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
560 OPTIONS.info_dict),
561 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
562 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700563
Doug Zongker05d3dea2009-06-22 11:32:31 -0700564 device_specific = common.DeviceSpecificParams(
565 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700566 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700567 output_zip=output_zip,
568 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700569 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700570 metadata=metadata,
571 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700572
Doug Zongkerc9253822014-02-04 12:17:58 -0800573 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800574 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800575
Tao Baob4cfca52016-02-04 14:26:02 -0800576 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
577
Doug Zongker962069c2009-04-23 11:41:58 -0700578 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700579 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700580 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
581 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700582
Michael Runge6e836112014-04-15 17:40:21 -0700583 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700584 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800585
586 # Two-step package strategy (in chronological order, which is *not*
587 # the order in which the generated script has things):
588 #
589 # if stage is not "2/3" or "3/3":
590 # write recovery image to boot partition
591 # set stage to "2/3"
592 # reboot to boot partition and restart recovery
593 # else if stage is "2/3":
594 # write recovery image to recovery partition
595 # set stage to "3/3"
596 # reboot to recovery partition and restart recovery
597 # else:
598 # (stage must be "3/3")
599 # set stage to ""
600 # do normal full package installation:
601 # wipe and install system, boot image, etc.
602 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700603 # complete script normally
604 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800605
606 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
607 OPTIONS.input_tmp, "RECOVERY")
608 if OPTIONS.two_step:
609 if not OPTIONS.info_dict.get("multistage_support", None):
610 assert False, "two-step packages not supported by this build"
611 fs = OPTIONS.info_dict["fstab"]["/misc"]
612 assert fs.fs_type.upper() == "EMMC", \
613 "two-step packages only supported on devices with EMMC /misc partitions"
614 bcb_dev = {"bcb_dev": fs.device}
615 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
616 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700617if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800618""" % bcb_dev)
619 script.WriteRawImage("/recovery", "recovery.img")
620 script.AppendExtra("""
621set_stage("%(bcb_dev)s", "3/3");
622reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700623else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800624""" % bcb_dev)
625
Tao Bao6c55a8a2015-04-08 15:30:27 -0700626 # Dump fingerprints
627 script.Print("Target: %s" % CalculateFingerprint(
628 oem_props, oem_dict, OPTIONS.info_dict))
629
Doug Zongkere5ff5902012-01-17 10:55:37 -0800630 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700631
Doug Zongker01ce19c2014-02-04 13:48:15 -0800632 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700633
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700634 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800635 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700636 if HasVendorPartition(input_zip):
637 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700638
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400639 # Place a copy of file_contexts.bin into the OTA package which will be used
640 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700641 if "selinux_fc" in OPTIONS.info_dict:
642 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500643
Michael Runge7cd99ba2014-10-22 17:21:48 -0700644 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
645
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700646 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700647 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800648
Doug Zongker26e66192014-02-20 13:22:07 -0800649 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700650 # Full OTA is done as an "incremental" against an empty source
651 # image. This has the effect of writing new data from the package
652 # to the entire partition, but lets us reuse the updater code that
653 # writes incrementals to do it.
654 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
655 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700656 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700657 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800658 else:
659 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700660 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800661 if not has_recovery_patch:
662 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800663 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700664
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700665 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800666 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700667
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700668 boot_img = common.GetBootableImage(
669 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800670
Doug Zongker91a99c22014-05-09 13:15:01 -0700671 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800672 def output_sink(fn, data):
673 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700674 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800675
676 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
677 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700678
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700679 system_items.GetMetadata(input_zip)
680 system_items.Get("system").SetPermissions(script)
681
682 if HasVendorPartition(input_zip):
683 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
684 script.ShowProgress(0.1, 0)
685
686 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700687 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
688 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700689 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700690 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700691 else:
692 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700693 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700694 script.UnpackPackageDir("vendor", "/vendor")
695
696 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
697 script.MakeSymlinks(symlinks)
698
699 vendor_items.GetMetadata(input_zip)
700 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700701
Doug Zongker37974732010-09-16 17:44:38 -0700702 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700703 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700704
Doug Zongker01ce19c2014-02-04 13:48:15 -0800705 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700706 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700707
Doug Zongker01ce19c2014-02-04 13:48:15 -0800708 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700709 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700710
Doug Zongker1c390a22009-05-14 19:06:36 -0700711 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700712 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700713
Doug Zongker14833602010-02-02 13:12:04 -0800714 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800715
Doug Zongker922206e2014-03-04 13:16:24 -0800716 if OPTIONS.wipe_user_data:
717 script.ShowProgress(0.1, 10)
718 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700719
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800720 if OPTIONS.two_step:
721 script.AppendExtra("""
722set_stage("%(bcb_dev)s", "");
723""" % bcb_dev)
724 script.AppendExtra("else\n")
725 script.WriteRawImage("/boot", "recovery.img")
726 script.AppendExtra("""
727set_stage("%(bcb_dev)s", "2/3");
728reboot_now("%(bcb_dev)s", "");
729endif;
730endif;
731""" % bcb_dev)
Tao Baob4cfca52016-02-04 14:26:02 -0800732
Tao Bao4da324e2016-02-23 11:38:39 -0800733 script.SetProgress(1)
734 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -0800735 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700736 WriteMetadata(metadata, output_zip)
737
Doug Zongkerfc44a512014-08-26 13:10:25 -0700738
Dan Albert8e0178d2015-01-27 15:53:15 -0800739def WritePolicyConfig(file_name, output_zip):
740 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500741
Doug Zongker2ea21062010-04-28 16:05:21 -0700742
743def WriteMetadata(metadata, output_zip):
744 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
745 "".join(["%s=%s\n" % kv
746 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700747
Doug Zongkerfc44a512014-08-26 13:10:25 -0700748
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700749def LoadPartitionFiles(z, partition):
750 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700751 ZipFile, and return a dict of {filename: File object}."""
752 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700753 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700754 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700755 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700756 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700757 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700758 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700759 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800760 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700761
762
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700763def GetBuildProp(prop, info_dict):
764 """Return the fingerprint of the build of a given target-files info_dict."""
765 try:
766 return info_dict.get("build.prop", {})[prop]
767 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700768 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700769
Doug Zongkerfc44a512014-08-26 13:10:25 -0700770
Michael Runge4038aa82013-12-13 18:06:28 -0800771def AddToKnownPaths(filename, known_paths):
772 if filename[-1] == "/":
773 return
774 dirs = filename.split("/")[:-1]
775 while len(dirs) > 0:
776 path = "/".join(dirs)
777 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700778 break
Michael Runge4038aa82013-12-13 18:06:28 -0800779 known_paths.add(path)
780 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700781
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700782
Geremy Condra36bd3652014-02-06 19:45:10 -0800783def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700784 # TODO(tbao): We should factor out the common parts between
785 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800786 source_version = OPTIONS.source_info_dict["recovery_api_version"]
787 target_version = OPTIONS.target_info_dict["recovery_api_version"]
788
789 if source_version == 0:
790 print ("WARNING: generating edify script for a source that "
791 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700792 script = edify_generator.EdifyGenerator(
793 source_version, OPTIONS.target_info_dict,
794 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800795
Tao Bao3806c232015-07-05 21:08:33 -0700796 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
797 recovery_mount_options = OPTIONS.source_info_dict.get(
798 "recovery_mount_options")
799 oem_dict = None
800 if oem_props is not None and len(oem_props) > 0:
801 if OPTIONS.oem_source is None:
802 raise common.ExternalError("OEM source required for this build")
Tao Baodf4cb0b2016-02-25 19:49:55 -0800803 if not OPTIONS.oem_no_mount:
804 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700805 oem_dict = common.LoadDictionaryFromLines(
806 open(OPTIONS.oem_source).readlines())
807
Dan Albert8b72aef2015-03-23 19:13:21 -0700808 metadata = {
Tao Bao3806c232015-07-05 21:08:33 -0700809 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
810 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -0800811 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700812 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800813
Tao Bao4da324e2016-02-23 11:38:39 -0800814 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
815 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
816 is_downgrade = long(post_timestamp) < long(pre_timestamp)
817
818 if OPTIONS.downgrade:
819 metadata["ota-downgrade"] = "yes"
820 if not is_downgrade:
821 raise RuntimeError("--downgrade specified but no downgrade detected: "
822 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
823 else:
824 if is_downgrade:
825 # Non-fatal here to allow generating such a package which may require
826 # manual work to adjust the post-timestamp. A legit use case is that we
827 # cut a new build C (after having A and B), but want to enfore the
828 # update path of A -> C -> B. Specifying --downgrade may not help since
829 # that would enforce a data wipe for C -> B update.
830 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
831 "The package may not be deployed properly. "
832 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
833 metadata["post-timestamp"] = post_timestamp
834
Geremy Condra36bd3652014-02-06 19:45:10 -0800835 device_specific = common.DeviceSpecificParams(
836 source_zip=source_zip,
837 source_version=source_version,
838 target_zip=target_zip,
839 target_version=target_version,
840 output_zip=output_zip,
841 script=script,
842 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700843 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800844
Tao Bao3806c232015-07-05 21:08:33 -0700845 source_fp = CalculateFingerprint(oem_props, oem_dict,
846 OPTIONS.source_info_dict)
847 target_fp = CalculateFingerprint(oem_props, oem_dict,
848 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800849 metadata["pre-build"] = source_fp
850 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700851 metadata["pre-build-incremental"] = GetBuildProp(
852 "ro.build.version.incremental", OPTIONS.source_info_dict)
853 metadata["post-build-incremental"] = GetBuildProp(
854 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800855
856 source_boot = common.GetBootableImage(
857 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
858 OPTIONS.source_info_dict)
859 target_boot = common.GetBootableImage(
860 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
861 updating_boot = (not OPTIONS.two_step and
862 (source_boot.data != target_boot.data))
863
Geremy Condra36bd3652014-02-06 19:45:10 -0800864 target_recovery = common.GetBootableImage(
865 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800866
Doug Zongkerfc44a512014-08-26 13:10:25 -0700867 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
868 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700869
870 blockimgdiff_version = 1
871 if OPTIONS.info_dict:
872 blockimgdiff_version = max(
873 int(i) for i in
874 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
875
Tao Baof8acad12016-07-07 09:09:58 -0700876 # Check the first block of the source system partition for remount R/W only
877 # if the filesystem is ext4.
878 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
879 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700880 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
881 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
882 # b) the blocks listed in block map may not contain all the bytes for a given
883 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700884 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
885 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
886 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700887 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800888 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700889 version=blockimgdiff_version,
890 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700891
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700892 if HasVendorPartition(target_zip):
893 if not HasVendorPartition(source_zip):
894 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700895 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
896 OPTIONS.source_info_dict)
897 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
898 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800899
900 # Check first block of vendor partition for remount R/W only if
901 # disk type is ext4
902 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baob4cfca52016-02-04 14:26:02 -0800903 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700904 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700905 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800906 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700907 version=blockimgdiff_version,
908 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700909 else:
910 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800911
Michael Rungec6e3afd2014-05-05 11:55:47 -0700912 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800913 device_specific.IncrementalOTA_Assertions()
914
915 # Two-step incremental package strategy (in chronological order,
916 # which is *not* the order in which the generated script has
917 # things):
918 #
919 # if stage is not "2/3" or "3/3":
920 # do verification on current system
921 # write recovery image to boot partition
922 # set stage to "2/3"
923 # reboot to boot partition and restart recovery
924 # else if stage is "2/3":
925 # write recovery image to recovery partition
926 # set stage to "3/3"
927 # reboot to recovery partition and restart recovery
928 # else:
929 # (stage must be "3/3")
930 # perform update:
931 # patch system files, etc.
932 # force full install of new boot image
933 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700934 # complete script normally
935 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800936
937 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700938 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800939 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700940 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800941 assert fs.fs_type.upper() == "EMMC", \
942 "two-step packages only supported on devices with EMMC /misc partitions"
943 bcb_dev = {"bcb_dev": fs.device}
944 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
945 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700946if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800947""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -0700948 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800949 script.WriteRawImage("/recovery", "recovery.img")
950 script.AppendExtra("""
951set_stage("%(bcb_dev)s", "3/3");
952reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700953else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800954""" % bcb_dev)
955
Tao Bao6c55a8a2015-04-08 15:30:27 -0700956 # Dump fingerprints
957 script.Print("Source: %s" % CalculateFingerprint(
958 oem_props, oem_dict, OPTIONS.source_info_dict))
959 script.Print("Target: %s" % CalculateFingerprint(
960 oem_props, oem_dict, OPTIONS.target_info_dict))
961
Geremy Condra36bd3652014-02-06 19:45:10 -0800962 script.Print("Verifying current system...")
963
964 device_specific.IncrementalOTA_VerifyBegin()
965
Michael Rungec6e3afd2014-05-05 11:55:47 -0700966 if oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700967 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
968 # patching on a device that's already on the target build will damage the
969 # system. Because operations like move don't check the block state, they
970 # always apply the changes unconditionally.
971 if blockimgdiff_version <= 2:
972 script.AssertSomeFingerprint(source_fp)
973 else:
974 script.AssertSomeFingerprint(source_fp, target_fp)
Michael Rungec6e3afd2014-05-05 11:55:47 -0700975 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700976 if blockimgdiff_version <= 2:
977 script.AssertSomeThumbprint(
978 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
979 else:
980 script.AssertSomeThumbprint(
981 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
982 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800983
Tao Baob4cfca52016-02-04 14:26:02 -0800984 # Check the required cache size (i.e. stashed blocks).
985 size = []
986 if system_diff:
987 size.append(system_diff.required_cache)
988 if vendor_diff:
989 size.append(vendor_diff.required_cache)
990
Geremy Condra36bd3652014-02-06 19:45:10 -0800991 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700992 boot_type, boot_device = common.GetTypeAndDevice(
993 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800994 d = common.Difference(target_boot, source_boot)
995 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700996 if d is None:
997 include_full_boot = True
998 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
999 else:
1000 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001001
Doug Zongkerf8340082014-08-05 10:39:37 -07001002 print "boot target: %d source: %d diff: %d" % (
1003 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001004
Doug Zongkerf8340082014-08-05 10:39:37 -07001005 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001006
Doug Zongkerf8340082014-08-05 10:39:37 -07001007 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1008 (boot_type, boot_device,
1009 source_boot.size, source_boot.sha1,
1010 target_boot.size, target_boot.sha1))
Tao Baob4cfca52016-02-04 14:26:02 -08001011 size.append(target_boot.size)
1012
1013 if size:
1014 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001015
1016 device_specific.IncrementalOTA_VerifyEnd()
1017
1018 if OPTIONS.two_step:
1019 script.WriteRawImage("/boot", "recovery.img")
1020 script.AppendExtra("""
1021set_stage("%(bcb_dev)s", "2/3");
1022reboot_now("%(bcb_dev)s", "");
1023else
1024""" % bcb_dev)
1025
Jesse Zhao75bcea02015-01-06 10:59:53 -08001026 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001027 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001028 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001029 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001030
Geremy Condra36bd3652014-02-06 19:45:10 -08001031 script.Comment("---- start making changes here ----")
1032
1033 device_specific.IncrementalOTA_InstallBegin()
1034
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001035 system_diff.WriteScript(script, output_zip,
1036 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001037
Doug Zongkerfc44a512014-08-26 13:10:25 -07001038 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001039 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001040
1041 if OPTIONS.two_step:
1042 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1043 script.WriteRawImage("/boot", "boot.img")
1044 print "writing full boot image (forced by two-step mode)"
1045
1046 if not OPTIONS.two_step:
1047 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001048 if include_full_boot:
1049 print "boot image changed; including full."
1050 script.Print("Installing boot image...")
1051 script.WriteRawImage("/boot", "boot.img")
1052 else:
1053 # Produce the boot image by applying a patch to the current
1054 # contents of the boot partition, and write it back to the
1055 # partition.
1056 print "boot image changed; including patch."
1057 script.Print("Patching boot image...")
1058 script.ShowProgress(0.1, 10)
1059 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1060 % (boot_type, boot_device,
1061 source_boot.size, source_boot.sha1,
1062 target_boot.size, target_boot.sha1),
1063 "-",
1064 target_boot.size, target_boot.sha1,
1065 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001066 else:
1067 print "boot image unchanged; skipping."
1068
1069 # Do device-specific installation (eg, write radio image).
1070 device_specific.IncrementalOTA_InstallEnd()
1071
1072 if OPTIONS.extra_script is not None:
1073 script.AppendExtra(OPTIONS.extra_script)
1074
Doug Zongker922206e2014-03-04 13:16:24 -08001075 if OPTIONS.wipe_user_data:
1076 script.Print("Erasing user data...")
1077 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001078 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001079
Geremy Condra36bd3652014-02-06 19:45:10 -08001080 if OPTIONS.two_step:
1081 script.AppendExtra("""
1082set_stage("%(bcb_dev)s", "");
1083endif;
1084endif;
1085""" % bcb_dev)
1086
1087 script.SetProgress(1)
Tao Baofa41fb22016-03-08 17:53:39 -08001088 # For downgrade OTAs, we prefer to use the update-binary in the source
1089 # build that is actually newer than the one in the target build.
1090 if OPTIONS.downgrade:
1091 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1092 else:
1093 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001094 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001095 WriteMetadata(metadata, output_zip)
1096
Doug Zongker32b527d2014-03-04 10:03:02 -08001097
Tao Bao9bc6bb22015-11-09 16:58:28 -08001098def WriteVerifyPackage(input_zip, output_zip):
1099 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1100
1101 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1102 recovery_mount_options = OPTIONS.info_dict.get(
1103 "recovery_mount_options")
1104 oem_dict = None
1105 if oem_props is not None and len(oem_props) > 0:
1106 if OPTIONS.oem_source is None:
1107 raise common.ExternalError("OEM source required for this build")
1108 script.Mount("/oem", recovery_mount_options)
1109 oem_dict = common.LoadDictionaryFromLines(
1110 open(OPTIONS.oem_source).readlines())
1111
1112 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1113 metadata = {
1114 "post-build": target_fp,
1115 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1116 OPTIONS.info_dict),
1117 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1118 }
1119
1120 device_specific = common.DeviceSpecificParams(
1121 input_zip=input_zip,
1122 input_version=OPTIONS.info_dict["recovery_api_version"],
1123 output_zip=output_zip,
1124 script=script,
1125 input_tmp=OPTIONS.input_tmp,
1126 metadata=metadata,
1127 info_dict=OPTIONS.info_dict)
1128
1129 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1130
1131 script.Print("Verifying device images against %s..." % target_fp)
1132 script.AppendExtra("")
1133
1134 script.Print("Verifying boot...")
1135 boot_img = common.GetBootableImage(
1136 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1137 boot_type, boot_device = common.GetTypeAndDevice(
1138 "/boot", OPTIONS.info_dict)
1139 script.Verify("%s:%s:%d:%s" % (
1140 boot_type, boot_device, boot_img.size, boot_img.sha1))
1141 script.AppendExtra("")
1142
1143 script.Print("Verifying recovery...")
1144 recovery_img = common.GetBootableImage(
1145 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1146 recovery_type, recovery_device = common.GetTypeAndDevice(
1147 "/recovery", OPTIONS.info_dict)
1148 script.Verify("%s:%s:%d:%s" % (
1149 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1150 script.AppendExtra("")
1151
1152 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1153 system_tgt.ResetFileMap()
1154 system_diff = common.BlockDifference("system", system_tgt, src=None)
1155 system_diff.WriteStrictVerifyScript(script)
1156
1157 if HasVendorPartition(input_zip):
1158 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1159 vendor_tgt.ResetFileMap()
1160 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1161 vendor_diff.WriteStrictVerifyScript(script)
1162
1163 # Device specific partitions, such as radio, bootloader and etc.
1164 device_specific.VerifyOTA_Assertions()
1165
1166 script.SetProgress(1.0)
1167 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001168 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001169 WriteMetadata(metadata, output_zip)
1170
1171
Tao Baoc098e9e2016-01-07 13:03:56 -08001172def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1173 source_file=None):
1174 """Generate an Android OTA package that has A/B update payload."""
1175
1176 # Setup signing keys.
1177 if OPTIONS.package_key is None:
1178 OPTIONS.package_key = OPTIONS.info_dict.get(
1179 "default_system_dev_certificate",
1180 "build/target/product/security/testkey")
1181
Tao Baodea0f8b2016-06-20 17:55:06 -07001182 # A/B updater expects a signing key in RSA format. Gets the key ready for
1183 # later use in step 3, unless a payload_signer has been specified.
1184 if OPTIONS.payload_signer is None:
1185 cmd = ["openssl", "pkcs8",
1186 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1187 "-inform", "DER", "-nocrypt"]
1188 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1189 cmd.extend(["-out", rsa_key])
1190 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1191 p1.wait()
1192 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001193
Tao Baodea0f8b2016-06-20 17:55:06 -07001194 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001195 temp_zip_file = tempfile.NamedTemporaryFile()
1196 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1197 compression=zipfile.ZIP_DEFLATED)
1198
1199 # Metadata to comply with Android OTA package format.
1200 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1201 oem_dict = None
1202 if oem_props:
1203 if OPTIONS.oem_source is None:
1204 raise common.ExternalError("OEM source required for this build")
1205 oem_dict = common.LoadDictionaryFromLines(
1206 open(OPTIONS.oem_source).readlines())
1207
1208 metadata = {
1209 "post-build": CalculateFingerprint(oem_props, oem_dict,
1210 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001211 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1212 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001213 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1214 OPTIONS.info_dict),
1215 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001216 "ota-required-cache": "0",
1217 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001218 }
1219
1220 if source_file is not None:
1221 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1222 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001223 metadata["pre-build-incremental"] = GetBuildProp(
1224 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001225
1226 # 1. Generate payload.
1227 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1228 cmd = ["brillo_update_payload", "generate",
1229 "--payload", payload_file,
1230 "--target_image", target_file]
1231 if source_file is not None:
1232 cmd.extend(["--source_image", source_file])
1233 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1234 p1.wait()
1235 assert p1.returncode == 0, "brillo_update_payload generate failed"
1236
1237 # 2. Generate hashes of the payload and metadata files.
1238 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1239 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1240 cmd = ["brillo_update_payload", "hash",
1241 "--unsigned_payload", payload_file,
1242 "--signature_size", "256",
1243 "--metadata_hash_file", metadata_sig_file,
1244 "--payload_hash_file", payload_sig_file]
1245 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1246 p1.wait()
1247 assert p1.returncode == 0, "brillo_update_payload hash failed"
1248
1249 # 3. Sign the hashes and insert them back into the payload file.
1250 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1251 suffix=".bin")
1252 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1253 suffix=".bin")
1254 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001255 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001256 cmd = [OPTIONS.payload_signer]
1257 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001258 else:
1259 cmd = ["openssl", "pkeyutl", "-sign",
1260 "-inkey", rsa_key,
1261 "-pkeyopt", "digest:sha256"]
1262 cmd.extend(["-in", payload_sig_file,
1263 "-out", signed_payload_sig_file])
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001264
Tao Baoc098e9e2016-01-07 13:03:56 -08001265 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1266 p1.wait()
1267 assert p1.returncode == 0, "openssl sign payload failed"
1268
1269 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001270 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001271 cmd = [OPTIONS.payload_signer]
1272 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001273 else:
1274 cmd = ["openssl", "pkeyutl", "-sign",
1275 "-inkey", rsa_key,
1276 "-pkeyopt", "digest:sha256"]
1277 cmd.extend(["-in", metadata_sig_file,
1278 "-out", signed_metadata_sig_file])
Tao Baoc098e9e2016-01-07 13:03:56 -08001279 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1280 p1.wait()
1281 assert p1.returncode == 0, "openssl sign metadata failed"
1282
1283 # 3c. Insert the signatures back into the payload file.
1284 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1285 suffix=".bin")
1286 cmd = ["brillo_update_payload", "sign",
1287 "--unsigned_payload", payload_file,
1288 "--payload", signed_payload_file,
1289 "--signature_size", "256",
1290 "--metadata_signature_file", signed_metadata_sig_file,
1291 "--payload_signature_file", signed_payload_sig_file]
1292 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1293 p1.wait()
1294 assert p1.returncode == 0, "brillo_update_payload sign failed"
1295
Alex Deymo19241c12016-02-04 22:29:29 -08001296 # 4. Dump the signed payload properties.
1297 properties_file = common.MakeTempFile(prefix="payload-properties-",
1298 suffix=".txt")
1299 cmd = ["brillo_update_payload", "properties",
1300 "--payload", signed_payload_file,
1301 "--properties_file", properties_file]
1302 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1303 p1.wait()
1304 assert p1.returncode == 0, "brillo_update_payload properties failed"
1305
Tao Bao38ca0be2016-06-14 17:48:11 -07001306 if OPTIONS.wipe_user_data:
1307 with open(properties_file, "a") as f:
1308 f.write("POWERWASH=1\n")
1309 metadata["ota-wipe"] = "yes"
1310
Alex Deymo19241c12016-02-04 22:29:29 -08001311 # Add the signed payload file and properties into the zip.
1312 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001313 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1314 compress_type=zipfile.ZIP_STORED)
1315 WriteMetadata(metadata, output_zip)
1316
Tianjie Xucfa86222016-03-07 16:31:19 -08001317 # If dm-verity is supported for the device, copy contents of care_map
1318 # into A/B OTA package.
1319 if OPTIONS.info_dict.get("verity") == "true":
1320 target_zip = zipfile.ZipFile(target_file, "r")
1321 care_map_path = "META/care_map.txt"
1322 namelist = target_zip.namelist()
1323 if care_map_path in namelist:
1324 care_map_data = target_zip.read(care_map_path)
1325 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data)
1326 else:
1327 print "Warning: cannot find care map file in target_file package"
1328 common.ZipClose(target_zip)
1329
Tao Baoc098e9e2016-01-07 13:03:56 -08001330 # Sign the whole package to comply with the Android OTA package format.
1331 common.ZipClose(output_zip)
1332 SignOutput(temp_zip_file.name, output_file)
1333 temp_zip_file.close()
1334
1335
Dan Albert8b72aef2015-03-23 19:13:21 -07001336class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001337 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001338 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001339 print "Loading target..."
1340 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1341 print "Loading source..."
1342 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1343
1344 self.verbatim_targets = verbatim_targets = []
1345 self.patch_list = patch_list = []
1346 diffs = []
1347 self.renames = renames = {}
1348 known_paths = set()
1349 largest_source_size = 0
1350
1351 matching_file_cache = {}
1352 for fn, sf in source_data.items():
1353 assert fn == sf.name
1354 matching_file_cache["path:" + fn] = sf
1355 if fn in target_data.keys():
1356 AddToKnownPaths(fn, known_paths)
1357 # Only allow eligibility for filename/sha matching
1358 # if there isn't a perfect path match.
1359 if target_data.get(sf.name) is None:
1360 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1361 matching_file_cache["sha:" + sf.sha1] = sf
1362
1363 for fn in sorted(target_data.keys()):
1364 tf = target_data[fn]
1365 assert fn == tf.name
1366 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1367 if sf is not None and sf.name != tf.name:
1368 print "File has moved from " + sf.name + " to " + tf.name
1369 renames[sf.name] = tf
1370
1371 if sf is None or fn in OPTIONS.require_verbatim:
1372 # This file should be included verbatim
1373 if fn in OPTIONS.prohibit_verbatim:
1374 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1375 print "send", fn, "verbatim"
1376 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001377 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001378 if fn in target_data.keys():
1379 AddToKnownPaths(fn, known_paths)
1380 elif tf.sha1 != sf.sha1:
1381 # File is different; consider sending as a patch
1382 diffs.append(common.Difference(tf, sf))
1383 else:
1384 # Target file data identical to source (may still be renamed)
1385 pass
1386
1387 common.ComputeDifferences(diffs)
1388
1389 for diff in diffs:
1390 tf, sf, d = diff.GetPatch()
1391 path = "/".join(tf.name.split("/")[:-1])
1392 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1393 path not in known_paths:
1394 # patch is almost as big as the file; don't bother patching
1395 # or a patch + rename cannot take place due to the target
1396 # directory not existing
1397 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001398 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001399 if sf.name in renames:
1400 del renames[sf.name]
1401 AddToKnownPaths(tf.name, known_paths)
1402 else:
1403 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1404 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1405 largest_source_size = max(largest_source_size, sf.size)
1406
1407 self.largest_source_size = largest_source_size
1408
1409 def EmitVerification(self, script):
1410 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001411 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001412 if tf.name != sf.name:
1413 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1414 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1415 so_far += sf.size
1416 return so_far
1417
Michael Runge63f01de2014-10-28 19:24:19 -07001418 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001419 for fn, _, sha1 in self.verbatim_targets:
1420 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001421 script.FileCheck("/"+fn, sha1)
1422 for tf, _, _, _ in self.patch_list:
1423 script.FileCheck(tf.name, tf.sha1)
1424
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001425 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001426 file_list = ["/" + i[0] for i in self.verbatim_targets]
1427 file_list += ["/" + i for i in self.source_data
1428 if i not in self.target_data and i not in self.renames]
1429 file_list += list(extras)
1430 # Sort the list in descending order, which removes all the files first
1431 # before attempting to remove the folder. (Bug: 22960996)
1432 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001433
1434 def TotalPatchSize(self):
1435 return sum(i[1].size for i in self.patch_list)
1436
1437 def EmitPatches(self, script, total_patch_size, so_far):
1438 self.deferred_patch_list = deferred_patch_list = []
1439 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001440 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001441 if tf.name == "system/build.prop":
1442 deferred_patch_list.append(item)
1443 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001444 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001445 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001446 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1447 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001448 so_far += tf.size
1449 script.SetProgress(so_far / total_patch_size)
1450 return so_far
1451
1452 def EmitDeferredPatches(self, script):
1453 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001454 tf, sf, _, _ = item
1455 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1456 "patch/" + sf.name + ".p")
1457 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001458
1459 def EmitRenames(self, script):
1460 if len(self.renames) > 0:
1461 script.Print("Renaming files...")
1462 for src, tgt in self.renames.iteritems():
1463 print "Renaming " + src + " to " + tgt.name
1464 script.RenameFile(src, tgt.name)
1465
1466
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001467def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001468 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1469 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1470
Doug Zongker26e66192014-02-20 13:22:07 -08001471 if (OPTIONS.block_based and
1472 target_has_recovery_patch and
1473 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001474 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1475
Doug Zongker37974732010-09-16 17:44:38 -07001476 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1477 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001478
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001479 if source_version == 0:
1480 print ("WARNING: generating edify script for a source that "
1481 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001482 script = edify_generator.EdifyGenerator(
1483 source_version, OPTIONS.target_info_dict,
1484 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001485
Michael Runge6e836112014-04-15 17:40:21 -07001486 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Tao Bao34b47bf2015-06-22 19:17:41 -07001487 recovery_mount_options = OPTIONS.source_info_dict.get(
1488 "recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -07001489 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -07001490 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -07001491 if OPTIONS.oem_source is None:
1492 raise common.ExternalError("OEM source required for this build")
Tao Baobd25fcd2016-03-07 21:24:40 -08001493 if not OPTIONS.oem_no_mount:
1494 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001495 oem_dict = common.LoadDictionaryFromLines(
1496 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001497
Dan Albert8b72aef2015-03-23 19:13:21 -07001498 metadata = {
1499 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1500 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001501 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001502 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001503
Tao Bao4da324e2016-02-23 11:38:39 -08001504 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
1505 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1506 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1507
1508 if OPTIONS.downgrade:
1509 metadata["ota-downgrade"] = "yes"
1510 if not is_downgrade:
1511 raise RuntimeError("--downgrade specified but no downgrade detected: "
1512 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
1513 else:
1514 if is_downgrade:
1515 # Non-fatal here to allow generating such a package which may require
1516 # manual work to adjust the post-timestamp. A legit use case is that we
1517 # cut a new build C (after having A and B), but want to enfore the
1518 # update path of A -> C -> B. Specifying --downgrade may not help since
1519 # that would enforce a data wipe for C -> B update.
1520 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
1521 "The package may not be deployed properly. "
1522 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
1523 metadata["post-timestamp"] = post_timestamp
1524
Doug Zongker05d3dea2009-06-22 11:32:31 -07001525 device_specific = common.DeviceSpecificParams(
1526 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001527 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001528 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001529 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001530 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001531 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001532 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001533 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001534
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001535 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001536 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001537 if HasVendorPartition(target_zip):
1538 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001539 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001540 else:
1541 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001542
Dan Albert8b72aef2015-03-23 19:13:21 -07001543 target_fp = CalculateFingerprint(oem_props, oem_dict,
1544 OPTIONS.target_info_dict)
1545 source_fp = CalculateFingerprint(oem_props, oem_dict,
1546 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001547
1548 if oem_props is None:
1549 script.AssertSomeFingerprint(source_fp, target_fp)
1550 else:
1551 script.AssertSomeThumbprint(
1552 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1553 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1554
Doug Zongker2ea21062010-04-28 16:05:21 -07001555 metadata["pre-build"] = source_fp
1556 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001557 metadata["pre-build-incremental"] = GetBuildProp(
1558 "ro.build.version.incremental", OPTIONS.source_info_dict)
1559 metadata["post-build-incremental"] = GetBuildProp(
1560 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001561
Doug Zongker55d93282011-01-25 17:03:34 -08001562 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001563 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1564 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001565 target_boot = common.GetBootableImage(
1566 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001567 updating_boot = (not OPTIONS.two_step and
1568 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001569
Doug Zongker55d93282011-01-25 17:03:34 -08001570 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001571 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1572 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001573 target_recovery = common.GetBootableImage(
1574 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001575 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001576
Doug Zongker881dd402009-09-20 14:03:55 -07001577 # Here's how we divide up the progress bar:
1578 # 0.1 for verifying the start state (PatchCheck calls)
1579 # 0.8 for applying patches (ApplyPatch calls)
1580 # 0.1 for unpacking verbatim files, symlinking, and doing the
1581 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001582
Michael Runge6e836112014-04-15 17:40:21 -07001583 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001584 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001585
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001586 # Two-step incremental package strategy (in chronological order,
1587 # which is *not* the order in which the generated script has
1588 # things):
1589 #
1590 # if stage is not "2/3" or "3/3":
1591 # do verification on current system
1592 # write recovery image to boot partition
1593 # set stage to "2/3"
1594 # reboot to boot partition and restart recovery
1595 # else if stage is "2/3":
1596 # write recovery image to recovery partition
1597 # set stage to "3/3"
1598 # reboot to recovery partition and restart recovery
1599 # else:
1600 # (stage must be "3/3")
1601 # perform update:
1602 # patch system files, etc.
1603 # force full install of new boot image
1604 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001605 # complete script normally
1606 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001607
1608 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001609 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001610 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001611 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001612 assert fs.fs_type.upper() == "EMMC", \
1613 "two-step packages only supported on devices with EMMC /misc partitions"
1614 bcb_dev = {"bcb_dev": fs.device}
1615 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1616 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001617if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001618""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -07001619 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001620 script.WriteRawImage("/recovery", "recovery.img")
1621 script.AppendExtra("""
1622set_stage("%(bcb_dev)s", "3/3");
1623reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001624else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001625""" % bcb_dev)
1626
Tao Bao6c55a8a2015-04-08 15:30:27 -07001627 # Dump fingerprints
1628 script.Print("Source: %s" % (source_fp,))
1629 script.Print("Target: %s" % (target_fp,))
1630
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001631 script.Print("Verifying current system...")
1632
Doug Zongkere5ff5902012-01-17 10:55:37 -08001633 device_specific.IncrementalOTA_VerifyBegin()
1634
Doug Zongker881dd402009-09-20 14:03:55 -07001635 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001636 so_far = system_diff.EmitVerification(script)
1637 if vendor_diff:
1638 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001639
Tao Baob4cfca52016-02-04 14:26:02 -08001640 size = []
1641 if system_diff.patch_list:
1642 size.append(system_diff.largest_source_size)
1643 if vendor_diff:
1644 if vendor_diff.patch_list:
1645 size.append(vendor_diff.largest_source_size)
1646
Doug Zongker5da317e2009-06-02 13:38:17 -07001647 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001648 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001649 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001650 print "boot target: %d source: %d diff: %d" % (
1651 target_boot.size, source_boot.size, len(d))
1652
Doug Zongker048e7ca2009-06-15 14:31:53 -07001653 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001654
Tao Baodd24da92015-07-29 14:09:23 -07001655 boot_type, boot_device = common.GetTypeAndDevice(
1656 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001657
1658 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1659 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001660 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001661 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001662 so_far += source_boot.size
Tao Baob4cfca52016-02-04 14:26:02 -08001663 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001664
Tao Baob4cfca52016-02-04 14:26:02 -08001665 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001666 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001667
Doug Zongker05d3dea2009-06-22 11:32:31 -07001668 device_specific.IncrementalOTA_VerifyEnd()
1669
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001670 if OPTIONS.two_step:
1671 script.WriteRawImage("/boot", "recovery.img")
1672 script.AppendExtra("""
1673set_stage("%(bcb_dev)s", "2/3");
1674reboot_now("%(bcb_dev)s", "");
1675else
1676""" % bcb_dev)
1677
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001678 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001679
Doug Zongkere5ff5902012-01-17 10:55:37 -08001680 device_specific.IncrementalOTA_InstallBegin()
1681
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001682 if OPTIONS.two_step:
1683 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1684 script.WriteRawImage("/boot", "boot.img")
1685 print "writing full boot image (forced by two-step mode)"
1686
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001687 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001688 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1689 if vendor_diff:
1690 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001691
Doug Zongker881dd402009-09-20 14:03:55 -07001692 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001693 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1694 if vendor_diff:
1695 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001696 if updating_boot:
1697 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001698
1699 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001700 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1701 if vendor_diff:
1702 script.Print("Patching vendor files...")
1703 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001704
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001705 if not OPTIONS.two_step:
1706 if updating_boot:
1707 # Produce the boot image by applying a patch to the current
1708 # contents of the boot partition, and write it back to the
1709 # partition.
1710 script.Print("Patching boot image...")
1711 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1712 % (boot_type, boot_device,
1713 source_boot.size, source_boot.sha1,
1714 target_boot.size, target_boot.sha1),
1715 "-",
1716 target_boot.size, target_boot.sha1,
1717 source_boot.sha1, "patch/boot.img.p")
1718 so_far += target_boot.size
1719 script.SetProgress(so_far / total_patch_size)
1720 print "boot image changed; including."
1721 else:
1722 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001723
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001724 system_items = ItemSet("system", "META/filesystem_config.txt")
1725 if vendor_diff:
1726 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1727
Doug Zongkereef39442009-04-02 12:14:19 -07001728 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001729 # Recovery is generated as a patch using both the boot image
1730 # (which contains the same linux kernel as recovery) and the file
1731 # /system/etc/recovery-resource.dat (which contains all the images
1732 # used in the recovery UI) as sources. This lets us minimize the
1733 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001734 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001735 # For older builds where recovery-resource.dat is not present, we
1736 # use only the boot image as the source.
1737
Doug Zongkerc9253822014-02-04 12:17:58 -08001738 if not target_has_recovery_patch:
1739 def output_sink(fn, data):
1740 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001741 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001742
1743 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1744 target_recovery, target_boot)
1745 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001746 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001747 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001748 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001749 else:
1750 print "recovery image unchanged; skipping."
1751
Doug Zongker881dd402009-09-20 14:03:55 -07001752 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001753
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001754 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1755 if vendor_diff:
1756 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1757
1758 temp_script = script.MakeTemporary()
1759 system_items.GetMetadata(target_zip)
1760 system_items.Get("system").SetPermissions(temp_script)
1761 if vendor_diff:
1762 vendor_items.GetMetadata(target_zip)
1763 vendor_items.Get("vendor").SetPermissions(temp_script)
1764
1765 # Note that this call will mess up the trees of Items, so make sure
1766 # we're done with them.
1767 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1768 if vendor_diff:
1769 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001770
1771 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001772 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1773
1774 # Delete all the symlinks in source that aren't in target. This
1775 # needs to happen before verbatim files are unpacked, in case a
1776 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001777
1778 # If a symlink in the source will be replaced by a regular file, we cannot
1779 # delete the symlink/file in case the package gets applied again. For such
1780 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1781 # (Bug: 23646151)
1782 replaced_symlinks = dict()
1783 if system_diff:
1784 for i in system_diff.verbatim_targets:
1785 replaced_symlinks["/%s" % (i[0],)] = i[2]
1786 if vendor_diff:
1787 for i in vendor_diff.verbatim_targets:
1788 replaced_symlinks["/%s" % (i[0],)] = i[2]
1789
1790 if system_diff:
1791 for tf in system_diff.renames.values():
1792 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1793 if vendor_diff:
1794 for tf in vendor_diff.renames.values():
1795 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1796
1797 always_delete = []
1798 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001799 for dest, link in source_symlinks:
1800 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001801 if link in replaced_symlinks:
1802 may_delete.append((link, replaced_symlinks[link]))
1803 else:
1804 always_delete.append(link)
1805 script.DeleteFiles(always_delete)
1806 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001807
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001808 if system_diff.verbatim_targets:
1809 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001810 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001811 if vendor_diff and vendor_diff.verbatim_targets:
1812 script.Print("Unpacking new vendor files...")
1813 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001814
Doug Zongkerc9253822014-02-04 12:17:58 -08001815 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001816 script.Print("Unpacking new recovery...")
1817 script.UnpackPackageDir("recovery", "/system")
1818
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001819 system_diff.EmitRenames(script)
1820 if vendor_diff:
1821 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001822
Doug Zongker05d3dea2009-06-22 11:32:31 -07001823 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001824
1825 # Create all the symlinks that don't already exist, or point to
1826 # somewhere different than what we want. Delete each symlink before
1827 # creating it, since the 'symlink' command won't overwrite.
1828 to_create = []
1829 for dest, link in target_symlinks:
1830 if link in source_symlinks_d:
1831 if dest != source_symlinks_d[link]:
1832 to_create.append((dest, link))
1833 else:
1834 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001835 script.DeleteFiles([i[1] for i in to_create])
1836 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001837
1838 # Now that the symlinks are created, we can set all the
1839 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001840 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001841
Doug Zongker881dd402009-09-20 14:03:55 -07001842 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001843 device_specific.IncrementalOTA_InstallEnd()
1844
Doug Zongker1c390a22009-05-14 19:06:36 -07001845 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001846 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001847
Doug Zongkere92f15a2011-08-26 13:46:40 -07001848 # Patch the build.prop file last, so if something fails but the
1849 # device can still come up, it appears to be the old build and will
1850 # get set the OTA package again to retry.
1851 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001852 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001853
Doug Zongker922206e2014-03-04 13:16:24 -08001854 if OPTIONS.wipe_user_data:
1855 script.Print("Erasing user data...")
1856 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001857 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001858
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001859 if OPTIONS.two_step:
1860 script.AppendExtra("""
1861set_stage("%(bcb_dev)s", "");
1862endif;
1863endif;
1864""" % bcb_dev)
1865
Michael Runge63f01de2014-10-28 19:24:19 -07001866 if OPTIONS.verify and system_diff:
1867 script.Print("Remounting and verifying system partition files...")
1868 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001869 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001870 system_diff.EmitExplicitTargetVerification(script)
1871
1872 if OPTIONS.verify and vendor_diff:
1873 script.Print("Remounting and verifying vendor partition files...")
1874 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001875 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001876 vendor_diff.EmitExplicitTargetVerification(script)
Tao Baofa41fb22016-03-08 17:53:39 -08001877
1878 # For downgrade OTAs, we prefer to use the update-binary in the source
1879 # build that is actually newer than the one in the target build.
1880 if OPTIONS.downgrade:
1881 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1882 else:
1883 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001884
Tao Baob4cfca52016-02-04 14:26:02 -08001885 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001886 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001887
1888
1889def main(argv):
1890
1891 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001892 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001893 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001894 elif o in ("-k", "--package_key"):
1895 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001896 elif o in ("-i", "--incremental_from"):
1897 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001898 elif o == "--full_radio":
1899 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001900 elif o == "--full_bootloader":
1901 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001902 elif o in ("-w", "--wipe_user_data"):
1903 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001904 elif o in ("-n", "--no_prereq"):
1905 OPTIONS.omit_prereq = True
Tao Bao4da324e2016-02-23 11:38:39 -08001906 elif o == "--downgrade":
1907 OPTIONS.downgrade = True
1908 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07001909 elif o in ("-o", "--oem_settings"):
1910 OPTIONS.oem_source = a
Tao Baodf4cb0b2016-02-25 19:49:55 -08001911 elif o == "--oem_no_mount":
1912 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001913 elif o in ("-e", "--extra_script"):
1914 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001915 elif o in ("-a", "--aslr_mode"):
1916 if a in ("on", "On", "true", "True", "yes", "Yes"):
1917 OPTIONS.aslr_mode = True
1918 else:
1919 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001920 elif o in ("-t", "--worker_threads"):
1921 if a.isdigit():
1922 OPTIONS.worker_threads = int(a)
1923 else:
1924 raise ValueError("Cannot parse value %r for option %r - only "
1925 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001926 elif o in ("-2", "--two_step"):
1927 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001928 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001929 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001930 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001931 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001932 elif o == "--block":
1933 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001934 elif o in ("-b", "--binary"):
1935 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001936 elif o in ("--no_fallback_to_full",):
1937 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001938 elif o == "--stash_threshold":
1939 try:
1940 OPTIONS.stash_threshold = float(a)
1941 except ValueError:
1942 raise ValueError("Cannot parse value %r for option %r - expecting "
1943 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001944 elif o == "--gen_verify":
1945 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001946 elif o == "--log_diff":
1947 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001948 elif o == "--payload_signer":
1949 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001950 elif o == "--payload_signer_args":
1951 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001952 else:
1953 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001954 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001955
1956 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001957 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001958 extra_long_opts=[
1959 "board_config=",
1960 "package_key=",
1961 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001962 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001963 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001964 "wipe_user_data",
1965 "no_prereq",
Tao Bao4da324e2016-02-23 11:38:39 -08001966 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07001967 "extra_script=",
1968 "worker_threads=",
1969 "aslr_mode=",
1970 "two_step",
1971 "no_signing",
1972 "block",
1973 "binary=",
1974 "oem_settings=",
Tao Baodf4cb0b2016-02-25 19:49:55 -08001975 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001976 "verify",
1977 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001978 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001979 "gen_verify",
1980 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001981 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001982 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001983 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001984
1985 if len(args) != 2:
1986 common.Usage(__doc__)
1987 sys.exit(1)
1988
Tao Bao4da324e2016-02-23 11:38:39 -08001989 if OPTIONS.downgrade:
1990 # Sanity check to enforce a data wipe.
1991 if not OPTIONS.wipe_user_data:
1992 raise ValueError("Cannot downgrade without a data wipe")
1993
1994 # We should only allow downgrading incrementals (as opposed to full).
1995 # Otherwise the device may go back from arbitrary build with this full
1996 # OTA package.
1997 if OPTIONS.incremental_source is None:
1998 raise ValueError("Cannot generate downgradable full OTAs - consider"
1999 "using --omit_prereq?")
2000
Tao Baoc098e9e2016-01-07 13:03:56 -08002001 # Load the dict file from the zip directly to have a peek at the OTA type.
2002 # For packages using A/B update, unzipping is not needed.
2003 input_zip = zipfile.ZipFile(args[0], "r")
2004 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2005 common.ZipClose(input_zip)
2006
2007 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2008
2009 if ab_update:
2010 if OPTIONS.incremental_source is not None:
2011 OPTIONS.target_info_dict = OPTIONS.info_dict
2012 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2013 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2014 common.ZipClose(source_zip)
2015
2016 if OPTIONS.verbose:
2017 print "--- target info ---"
2018 common.DumpInfoDict(OPTIONS.info_dict)
2019
2020 if OPTIONS.incremental_source is not None:
2021 print "--- source info ---"
2022 common.DumpInfoDict(OPTIONS.source_info_dict)
2023
2024 WriteABOTAPackageWithBrilloScript(
2025 target_file=args[0],
2026 output_file=args[1],
2027 source_file=OPTIONS.incremental_source)
2028
2029 print "done."
2030 return
2031
Doug Zongker1c390a22009-05-14 19:06:36 -07002032 if OPTIONS.extra_script is not None:
2033 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2034
Doug Zongkereef39442009-04-02 12:14:19 -07002035 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08002036 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002037
Doug Zongkereef39442009-04-02 12:14:19 -07002038 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002039 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002040
Doug Zongker37974732010-09-16 17:44:38 -07002041 if OPTIONS.verbose:
2042 print "--- target info ---"
2043 common.DumpInfoDict(OPTIONS.info_dict)
2044
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002045 # If the caller explicitly specified the device-specific extensions
2046 # path via -s/--device_specific, use that. Otherwise, use
2047 # META/releasetools.py if it is present in the target target_files.
2048 # Otherwise, take the path of the file from 'tool_extensions' in the
2049 # info dict and look for that in the local filesystem, relative to
2050 # the current directory.
2051
Doug Zongker37974732010-09-16 17:44:38 -07002052 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002053 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2054 if os.path.exists(from_input):
2055 print "(using device-specific extensions from target_files)"
2056 OPTIONS.device_specific = from_input
2057 else:
2058 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2059
Doug Zongker37974732010-09-16 17:44:38 -07002060 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002061 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002062
Tao Baoc098e9e2016-01-07 13:03:56 -08002063 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002064 raise common.ExternalError(
2065 "--- target build has specified no recovery ---")
2066
Tao Bao767e3ac2015-11-10 12:19:19 -08002067 # Use the default key to sign the package if not specified with package_key.
2068 if not OPTIONS.no_signing:
2069 if OPTIONS.package_key is None:
2070 OPTIONS.package_key = OPTIONS.info_dict.get(
2071 "default_system_dev_certificate",
2072 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002073
Tao Bao767e3ac2015-11-10 12:19:19 -08002074 # Set up the output zip. Create a temporary zip file if signing is needed.
2075 if OPTIONS.no_signing:
2076 if os.path.exists(args[1]):
2077 os.unlink(args[1])
2078 output_zip = zipfile.ZipFile(args[1], "w",
2079 compression=zipfile.ZIP_DEFLATED)
2080 else:
2081 temp_zip_file = tempfile.NamedTemporaryFile()
2082 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2083 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002084
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002085 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002086 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002087 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002088 print "--- can't determine the cache partition size ---"
2089 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002090
Tao Bao9bc6bb22015-11-09 16:58:28 -08002091 # Generate a verify package.
2092 if OPTIONS.gen_verify:
2093 WriteVerifyPackage(input_zip, output_zip)
2094
Tao Bao767e3ac2015-11-10 12:19:19 -08002095 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002096 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002097 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002098
2099 # Generate an incremental OTA. It will fall back to generate a full OTA on
2100 # failure unless no_fallback_to_full is specified.
2101 else:
2102 print "unzipping source target-files..."
2103 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2104 OPTIONS.incremental_source)
2105 OPTIONS.target_info_dict = OPTIONS.info_dict
2106 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2107 OPTIONS.source_tmp)
2108 if OPTIONS.verbose:
2109 print "--- source info ---"
2110 common.DumpInfoDict(OPTIONS.source_info_dict)
2111 try:
2112 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002113 if OPTIONS.log_diff:
2114 out_file = open(OPTIONS.log_diff, 'w')
2115 import target_files_diff
2116 target_files_diff.recursiveDiff('',
2117 OPTIONS.source_tmp,
2118 OPTIONS.input_tmp,
2119 out_file)
2120 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002121 except ValueError:
2122 if not OPTIONS.fallback_to_full:
2123 raise
2124 print "--- failed to build incremental; falling back to full ---"
2125 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002126 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002127
Tao Bao767e3ac2015-11-10 12:19:19 -08002128 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002129
Tao Bao767e3ac2015-11-10 12:19:19 -08002130 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002131 if not OPTIONS.no_signing:
2132 SignOutput(temp_zip_file.name, args[1])
2133 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002134
Doug Zongkereef39442009-04-02 12:14:19 -07002135 print "done."
2136
2137
2138if __name__ == '__main__':
2139 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002140 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002141 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002142 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002143 print
2144 print " ERROR: %s" % (e,)
2145 print
2146 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002147 finally:
2148 common.Cleanup()