blob: 4292c71f45a21a7d4b5a4a261ded3a1b235021b0 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given a target-files zipfile, produces an OTA package that installs
19that build. An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage: ota_from_target_files [flags] input_target_files output_ota_package
23
Doug Zongker25568482014-03-03 10:21:27 -080024 --board_config <file>
Doug Zongkerfdd8e692009-08-03 17:27:48 -070025 Deprecated.
Doug Zongkereef39442009-04-02 12:14:19 -070026
Doug Zongkerafb32ea2011-09-22 10:28:04 -070027 -k (--package_key) <key> Key to use to sign the package (default is
28 the value of default_system_dev_certificate from the input
29 target-files's META/misc_info.txt, or
30 "build/target/product/security/testkey" if that value is not
31 specified).
32
33 For incremental OTAs, the default value is based on the source
34 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070035
36 -i (--incremental_from) <file>
37 Generate an incremental OTA using the given target-files zip as
38 the starting build.
39
Tao Bao43078aa2015-04-21 14:32:35 -070040 --full_radio
41 When generating an incremental OTA, always include a full copy of
42 radio image. This option is only meaningful when -i is specified,
43 because a full radio is always included in a full OTA if applicable.
44
leozwangaa6c1a12015-08-14 10:57:58 -070045 --full_bootloader
46 Similar to --full_radio. When generating an incremental OTA, always
47 include a full copy of bootloader image.
48
Michael Runge63f01de2014-10-28 19:24:19 -070049 -v (--verify)
50 Remount and verify the checksums of the files written to the
51 system and vendor (if used) partitions. Incremental builds only.
52
Michael Runge6e836112014-04-15 17:40:21 -070053 -o (--oem_settings) <file>
54 Use the file to specify the expected OEM-specific properties
55 on the OEM partition of the intended device.
56
Tao Bao8608cde2016-02-25 19:49:55 -080057 --oem_no_mount
58 For devices with OEM-specific properties but without an OEM partition,
59 do not mount the OEM partition in the updater-script. This should be
60 very rarely used, since it's expected to have a dedicated OEM partition
61 for OEM-specific properties. Only meaningful when -o is specified.
62
Doug Zongkerdbfaae52009-04-21 17:12:54 -070063 -w (--wipe_user_data)
64 Generate an OTA package that will wipe the user data partition
65 when installed.
66
Tao Bao5d182562016-02-23 11:38:39 -080067 --downgrade
68 Intentionally generate an incremental OTA that updates from a newer
69 build to an older one (based on timestamp comparison). "post-timestamp"
70 will be replaced by "ota-downgrade=yes" in the metadata file. A data
71 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080072 the metadata file. The update-binary in the source build will be used in
73 the OTA package, unless --binary flag is specified.
Tao Bao5d182562016-02-23 11:38:39 -080074
Doug Zongker1c390a22009-05-14 19:06:36 -070075 -e (--extra_script) <file>
76 Insert the contents of file at the end of the update script.
77
Hristo Bojinovdafb0422010-08-26 14:35:16 -070078 -a (--aslr_mode) <on|off>
79 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050080
Doug Zongker9b23f2c2013-11-25 14:44:12 -080081 -2 (--two_step)
82 Generate a 'two-step' OTA package, where recovery is updated
83 first, so that any changes made to the system partition are done
84 using the new recovery (new kernel, etc.).
85
Doug Zongker26e66192014-02-20 13:22:07 -080086 --block
87 Generate a block-based OTA if possible. Will fall back to a
88 file-based OTA if the target_files is older and doesn't support
89 block-based OTAs.
90
Doug Zongker25568482014-03-03 10:21:27 -080091 -b (--binary) <file>
92 Use the given binary as the update-binary in the output package,
93 instead of the binary in the build's target_files. Use for
94 development only.
95
Martin Blumenstingl374e1142014-05-31 20:42:55 +020096 -t (--worker_threads) <int>
97 Specifies the number of worker-threads that will be used when
98 generating patches for incremental updates (defaults to 3).
99
Tao Bao8dcf7382015-05-21 14:09:49 -0700100 --stash_threshold <float>
101 Specifies the threshold that will be used to compute the maximum
102 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800103
104 --gen_verify
105 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800106
107 --log_diff <file>
108 Generate a log file that shows the differences in the source and target
109 builds for an incremental package. This option is only meaningful when
110 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700111
112 --payload_signer <signer>
113 Specify the signer when signing the payload and metadata for A/B OTAs.
114 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
115 with the package private key. If the private key cannot be accessed
116 directly, a payload signer that knows how to do that should be specified.
117 The signer will be supplied with "-inkey <path_to_key>",
118 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700119
120 --payload_signer_args <args>
121 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700122"""
123
Tao Bao89fbb0f2017-01-10 10:47:58 -0800124from __future__ import print_function
125
Doug Zongkereef39442009-04-02 12:14:19 -0700126import sys
127
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800128if sys.hexversion < 0x02070000:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800129 print("Python 2.7 or newer is required.", file=sys.stderr)
Doug Zongkereef39442009-04-02 12:14:19 -0700130 sys.exit(1)
131
Doug Zongkerfc44a512014-08-26 13:10:25 -0700132import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700133import os
Tao Baoc098e9e2016-01-07 13:03:56 -0800134import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700135import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700136import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700137import zipfile
138
139import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700140import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700141import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700142
143OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700144OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700145OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700146OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700147OPTIONS.require_verbatim = set()
148OPTIONS.prohibit_verbatim = set(("system/build.prop",))
149OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700150OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800151OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700152OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700153OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700154OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
155if OPTIONS.worker_threads == 0:
156 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800157OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900158OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800159OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800160OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700161OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800162OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700163OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700164OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700165OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700166# Stash size cannot exceed cache_size * threshold.
167OPTIONS.cache_size = None
168OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800169OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800170OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700171OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700172OPTIONS.payload_signer_args = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700173
Doug Zongkereef39442009-04-02 12:14:19 -0700174def MostPopularKey(d, default):
175 """Given a dict, return the key corresponding to the largest
176 value. Returns 'default' if the dict is empty."""
177 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700178 if not x:
179 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700180 x.sort()
181 return x[-1][1]
182
183
184def IsSymlink(info):
185 """Return true if the zipfile.ZipInfo object passed in represents a
186 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700187 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700188
Hristo Bojinov96be7202010-08-02 10:26:17 -0700189def IsRegular(info):
190 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700191 regular file."""
192 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700193
Michael Runge4038aa82013-12-13 18:06:28 -0800194def ClosestFileMatch(src, tgtfiles, existing):
195 """Returns the closest file match between a source file and list
196 of potential matches. The exact filename match is preferred,
197 then the sha1 is searched for, and finally a file with the same
198 basename is evaluated. Rename support in the updater-binary is
199 required for the latter checks to be used."""
200
201 result = tgtfiles.get("path:" + src.name)
202 if result is not None:
203 return result
204
205 if not OPTIONS.target_info_dict.get("update_rename_support", False):
206 return None
207
208 if src.size < 1000:
209 return None
210
211 result = tgtfiles.get("sha1:" + src.sha1)
212 if result is not None and existing.get(result.name) is None:
213 return result
214 result = tgtfiles.get("file:" + src.name.split("/")[-1])
215 if result is not None and existing.get(result.name) is None:
216 return result
217 return None
218
Dan Albert8b72aef2015-03-23 19:13:21 -0700219class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700220 def __init__(self, partition, fs_config):
221 self.partition = partition
222 self.fs_config = fs_config
223 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700224
Dan Albert8b72aef2015-03-23 19:13:21 -0700225 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700226 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700227 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700228 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700229
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700230 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700231 # The target_files contains a record of what the uid,
232 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700233 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700234
235 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700236 if not line:
237 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700238 columns = line.split()
239 name, uid, gid, mode = columns[:4]
240 selabel = None
241 capabilities = None
242
243 # After the first 4 columns, there are a series of key=value
244 # pairs. Extract out the fields we care about.
245 for element in columns[4:]:
246 key, value = element.split("=")
247 if key == "selabel":
248 selabel = value
249 if key == "capabilities":
250 capabilities = value
251
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700252 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700253 if i is not None:
254 i.uid = int(uid)
255 i.gid = int(gid)
256 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700257 i.selabel = selabel
258 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700259 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700260 i.children.sort(key=lambda i: i.name)
261
Tao Baof2cffbd2015-07-22 12:33:18 -0700262 # Set metadata for the files generated by this script. For full recovery
263 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700264 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700265 if i:
266 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700267 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700268 if i:
269 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700270
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700271
Dan Albert8b72aef2015-03-23 19:13:21 -0700272class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700273 """Items represent the metadata (user, group, mode) of files and
274 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700275 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700276 self.itemset = itemset
277 self.name = name
278 self.uid = None
279 self.gid = None
280 self.mode = None
281 self.selabel = None
282 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700283 self.is_dir = is_dir
284 self.descendants = None
285 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700286
287 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700288 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700289 self.parent.children.append(self)
290 else:
291 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700292 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700293 self.children = []
294
295 def Dump(self, indent=0):
296 if self.uid is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800297 print("%s%s %d %d %o" % (
298 " " * indent, self.name, self.uid, self.gid, self.mode))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700299 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800300 print("%s%s %s %s %s" % (
301 " " * indent, self.name, self.uid, self.gid, self.mode))
Dan Albert8b72aef2015-03-23 19:13:21 -0700302 if self.is_dir:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800303 print("%s%s" % (" " * indent, self.descendants))
304 print("%s%s" % (" " * indent, self.best_subtree))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700305 for i in self.children:
306 i.Dump(indent=indent+1)
307
Doug Zongkereef39442009-04-02 12:14:19 -0700308 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700309 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700310 all children and determine the best strategy for using set_perm_recursive
311 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700312 values. Recursively calls itself for all descendants.
313
Dan Albert8b72aef2015-03-23 19:13:21 -0700314 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
315 counting up all descendants of this node. (dmode or fmode may be None.)
316 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
317 fmode, selabel, capabilities) tuple that will match the most descendants of
318 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700319 """
320
Dan Albert8b72aef2015-03-23 19:13:21 -0700321 assert self.is_dir
322 key = (self.uid, self.gid, self.mode, None, self.selabel,
323 self.capabilities)
324 self.descendants = {key: 1}
325 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700326 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700327 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700328 for k, v in i.CountChildMetadata().iteritems():
329 d[k] = d.get(k, 0) + v
330 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700331 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700332 d[k] = d.get(k, 0) + 1
333
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700334 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
335 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700336
337 # First, find the (uid, gid) pair that matches the most
338 # descendants.
339 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700340 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700341 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
342 ug = MostPopularKey(ug, (0, 0))
343
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700344 # Now find the dmode, fmode, selabel, and capabilities that match
345 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700346 best_dmode = (0, 0o755)
347 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700348 best_selabel = (0, None)
349 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700350 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700351 if k[:2] != ug:
352 continue
353 if k[2] is not None and count >= best_dmode[0]:
354 best_dmode = (count, k[2])
355 if k[3] is not None and count >= best_fmode[0]:
356 best_fmode = (count, k[3])
357 if k[4] is not None and count >= best_selabel[0]:
358 best_selabel = (count, k[4])
359 if k[5] is not None and count >= best_capabilities[0]:
360 best_capabilities = (count, k[5])
361 self.best_subtree = ug + (
362 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700363
364 return d
365
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700366 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700367 """Append set_perm/set_perm_recursive commands to 'script' to
368 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700369 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700370
371 self.CountChildMetadata()
372
373 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700374 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
375 # that the current item (and all its children) have already been set to.
376 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700377 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700378 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700379 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700380 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700381 current = item.best_subtree
382
383 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700384 item.mode != current[2] or item.selabel != current[4] or \
385 item.capabilities != current[5]:
386 script.SetPermissions("/"+item.name, item.uid, item.gid,
387 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700388
389 for i in item.children:
390 recurse(i, current)
391 else:
392 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700393 item.mode != current[3] or item.selabel != current[4] or \
394 item.capabilities != current[5]:
395 script.SetPermissions("/"+item.name, item.uid, item.gid,
396 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700397
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700398 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700399
400
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700401def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
402 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700403 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800404 list of symlinks. output_zip may be None, in which case the copy is
405 skipped (but the other side effects still happen). substitute is an
406 optional dict of {output filename: contents} to be output instead of
407 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700408 """
409
410 symlinks = []
411
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700412 partition = itemset.partition
413
Doug Zongkereef39442009-04-02 12:14:19 -0700414 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700415 prefix = partition.upper() + "/"
416 if info.filename.startswith(prefix):
417 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700418 if IsSymlink(info):
419 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700420 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700421 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700422 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700423 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700424 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700425 if substitute and fn in substitute and substitute[fn] is None:
426 continue
427 if output_zip is not None:
428 if substitute and fn in substitute:
429 data = substitute[fn]
430 else:
431 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700432 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700433 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700434 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700435 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700436 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700437
438 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800439 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700440
441
Doug Zongkereef39442009-04-02 12:14:19 -0700442def SignOutput(temp_zip_name, output_zip_name):
443 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
444 pw = key_passwords[OPTIONS.package_key]
445
Doug Zongker951495f2009-08-14 12:44:19 -0700446 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
447 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700448
449
Dan Albert8b72aef2015-03-23 19:13:21 -0700450def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700451 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700452 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700453 device = GetBuildProp("ro.product.device", info_dict)
454 script.AssertDevice(device)
455 else:
456 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700457 raise common.ExternalError(
458 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700459 for prop in oem_props.split():
460 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700461 raise common.ExternalError(
462 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700463 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700464
Doug Zongkereef39442009-04-02 12:14:19 -0700465
Tao Baod42e97e2016-11-30 12:11:57 -0800466def _WriteRecoveryImageToBoot(script, output_zip):
467 """Find and write recovery image to /boot in two-step OTA.
468
469 In two-step OTAs, we write recovery image to /boot as the first step so that
470 we can reboot to there and install a new recovery image to /recovery.
471 A special "recovery-two-step.img" will be preferred, which encodes the correct
472 path of "/boot". Otherwise the device may show "device is corrupt" message
473 when booting into /boot.
474
475 Fall back to using the regular recovery.img if the two-step recovery image
476 doesn't exist. Note that rebuilding the special image at this point may be
477 infeasible, because we don't have the desired boot signer and keys when
478 calling ota_from_target_files.py.
479 """
480
481 recovery_two_step_img_name = "recovery-two-step.img"
482 recovery_two_step_img_path = os.path.join(
483 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
484 if os.path.exists(recovery_two_step_img_path):
485 recovery_two_step_img = common.GetBootableImage(
486 recovery_two_step_img_name, recovery_two_step_img_name,
487 OPTIONS.input_tmp, "RECOVERY")
488 common.ZipWriteStr(
489 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800490 print("two-step package: using %s in stage 1/3" % (
491 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800492 script.WriteRawImage("/boot", recovery_two_step_img_name)
493 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800494 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800495 # The "recovery.img" entry has been written into package earlier.
496 script.WriteRawImage("/boot", "recovery.img")
497
498
Doug Zongkerc9253822014-02-04 12:17:58 -0800499def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700500 namelist = [name for name in target_files_zip.namelist()]
501 return ("SYSTEM/recovery-from-boot.p" in namelist or
502 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700503
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700504def HasVendorPartition(target_files_zip):
505 try:
506 target_files_zip.getinfo("VENDOR/")
507 return True
508 except KeyError:
509 return False
510
Michael Runge6e836112014-04-15 17:40:21 -0700511def GetOemProperty(name, oem_props, oem_dict, info_dict):
512 if oem_props is not None and name in oem_props:
513 return oem_dict[name]
514 return GetBuildProp(name, info_dict)
515
516
517def CalculateFingerprint(oem_props, oem_dict, info_dict):
518 if oem_props is None:
519 return GetBuildProp("ro.build.fingerprint", info_dict)
520 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700521 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
522 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
523 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
524 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700525
Doug Zongkerfc44a512014-08-26 13:10:25 -0700526
Doug Zongker3c84f562014-07-31 11:06:30 -0700527def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700528 # Return an image object (suitable for passing to BlockImageDiff)
529 # for the 'which' partition (most be "system" or "vendor"). If a
530 # prebuilt image and file map are found in tmpdir they are used,
531 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700532
533 assert which in ("system", "vendor")
534
535 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700536 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
537 if os.path.exists(path) and os.path.exists(mappath):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800538 print("using %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700539 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700540
541 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800542 print("building %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700543
544 # This is an 'old' target-files, which does not contain images
545 # already built. Build them.
546
Doug Zongkerfc44a512014-08-26 13:10:25 -0700547 mappath = tempfile.mkstemp()[1]
548 OPTIONS.tempfiles.append(mappath)
549
Doug Zongker3c84f562014-07-31 11:06:30 -0700550 import add_img_to_target_files
551 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700552 path = add_img_to_target_files.BuildSystem(
553 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700554 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700555 path = add_img_to_target_files.BuildVendor(
556 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700557
Tao Baoff777812015-05-12 11:42:31 -0700558 # Bug: http://b/20939131
559 # In ext4 filesystems, block 0 might be changed even being mounted
560 # R/O. We add it to clobbered_blocks so that it will be written to the
561 # target unconditionally. Note that they are still part of care_map.
562 clobbered_blocks = "0"
563
564 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700565
566
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700567def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700568 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700569 # be installed on top of. For now, we expect the API just won't
570 # change very often. Similarly for fstab, it might have changed
571 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700572 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700573
Tao Bao838c68f2016-03-15 19:16:18 +0000574 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700575 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -0700576 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700577 if oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700578 if OPTIONS.oem_source is None:
579 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800580 if not OPTIONS.oem_no_mount:
581 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700582 oem_dict = common.LoadDictionaryFromLines(
583 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700584
Tao Bao3e30d972016-03-15 13:20:19 -0700585 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700586 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700587 "post-build": target_fp,
Dan Albert8b72aef2015-03-23 19:13:21 -0700588 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
589 OPTIONS.info_dict),
590 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
591 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700592
Doug Zongker05d3dea2009-06-22 11:32:31 -0700593 device_specific = common.DeviceSpecificParams(
594 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700595 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700596 output_zip=output_zip,
597 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700598 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700599 metadata=metadata,
600 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700601
Doug Zongkerc9253822014-02-04 12:17:58 -0800602 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800603 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800604
Tao Baod8d14be2016-02-04 14:26:02 -0800605 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
606
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700607 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
608 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
609 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700610
Michael Runge6e836112014-04-15 17:40:21 -0700611 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700612 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800613
614 # Two-step package strategy (in chronological order, which is *not*
615 # the order in which the generated script has things):
616 #
617 # if stage is not "2/3" or "3/3":
618 # write recovery image to boot partition
619 # set stage to "2/3"
620 # reboot to boot partition and restart recovery
621 # else if stage is "2/3":
622 # write recovery image to recovery partition
623 # set stage to "3/3"
624 # reboot to recovery partition and restart recovery
625 # else:
626 # (stage must be "3/3")
627 # set stage to ""
628 # do normal full package installation:
629 # wipe and install system, boot image, etc.
630 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700631 # complete script normally
632 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800633
634 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
635 OPTIONS.input_tmp, "RECOVERY")
636 if OPTIONS.two_step:
637 if not OPTIONS.info_dict.get("multistage_support", None):
638 assert False, "two-step packages not supported by this build"
639 fs = OPTIONS.info_dict["fstab"]["/misc"]
640 assert fs.fs_type.upper() == "EMMC", \
641 "two-step packages only supported on devices with EMMC /misc partitions"
642 bcb_dev = {"bcb_dev": fs.device}
643 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
644 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700645if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800646""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800647
648 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
649 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800650 script.WriteRawImage("/recovery", "recovery.img")
651 script.AppendExtra("""
652set_stage("%(bcb_dev)s", "3/3");
653reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700654else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800655""" % bcb_dev)
656
Tao Baod42e97e2016-11-30 12:11:57 -0800657 # Stage 3/3: Make changes.
658 script.Comment("Stage 3/3")
659
Tao Bao6c55a8a2015-04-08 15:30:27 -0700660 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700661 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700662
Doug Zongkere5ff5902012-01-17 10:55:37 -0800663 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700664
Doug Zongker01ce19c2014-02-04 13:48:15 -0800665 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700666
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700667 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800668 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700669 if HasVendorPartition(input_zip):
670 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700671
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400672 # Place a copy of file_contexts.bin into the OTA package which will be used
673 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700674 if "selinux_fc" in OPTIONS.info_dict:
675 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500676
Michael Runge7cd99ba2014-10-22 17:21:48 -0700677 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
678
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700679 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700680 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800681
Doug Zongker26e66192014-02-20 13:22:07 -0800682 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700683 # Full OTA is done as an "incremental" against an empty source
684 # image. This has the effect of writing new data from the package
685 # to the entire partition, but lets us reuse the updater code that
686 # writes incrementals to do it.
687 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
688 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700689 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700690 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800691 else:
692 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700693 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800694 if not has_recovery_patch:
695 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800696 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700697
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700698 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800699 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700700
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700701 boot_img = common.GetBootableImage(
702 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800703
Doug Zongker91a99c22014-05-09 13:15:01 -0700704 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800705 def output_sink(fn, data):
706 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700707 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800708
709 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
710 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700711
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700712 system_items.GetMetadata(input_zip)
713 system_items.Get("system").SetPermissions(script)
714
715 if HasVendorPartition(input_zip):
716 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
717 script.ShowProgress(0.1, 0)
718
719 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700720 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
721 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700722 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700723 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700724 else:
725 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700726 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700727 script.UnpackPackageDir("vendor", "/vendor")
728
729 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
730 script.MakeSymlinks(symlinks)
731
732 vendor_items.GetMetadata(input_zip)
733 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700734
Doug Zongker37974732010-09-16 17:44:38 -0700735 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700736 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700737
Doug Zongker01ce19c2014-02-04 13:48:15 -0800738 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700739 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700740
Doug Zongker01ce19c2014-02-04 13:48:15 -0800741 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700742 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700743
Doug Zongker1c390a22009-05-14 19:06:36 -0700744 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700745 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700746
Doug Zongker14833602010-02-02 13:12:04 -0800747 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800748
Doug Zongker922206e2014-03-04 13:16:24 -0800749 if OPTIONS.wipe_user_data:
750 script.ShowProgress(0.1, 10)
751 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700752
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800753 if OPTIONS.two_step:
754 script.AppendExtra("""
755set_stage("%(bcb_dev)s", "");
756""" % bcb_dev)
757 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800758
759 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
760 script.Comment("Stage 1/3")
761 _WriteRecoveryImageToBoot(script, output_zip)
762
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800763 script.AppendExtra("""
764set_stage("%(bcb_dev)s", "2/3");
765reboot_now("%(bcb_dev)s", "");
766endif;
767endif;
768""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800769
Tao Bao5d182562016-02-23 11:38:39 -0800770 script.SetProgress(1)
771 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800772 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700773 WriteMetadata(metadata, output_zip)
774
Doug Zongkerfc44a512014-08-26 13:10:25 -0700775
Dan Albert8e0178d2015-01-27 15:53:15 -0800776def WritePolicyConfig(file_name, output_zip):
777 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500778
Doug Zongker2ea21062010-04-28 16:05:21 -0700779
780def WriteMetadata(metadata, output_zip):
781 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
782 "".join(["%s=%s\n" % kv
783 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700784
Doug Zongkerfc44a512014-08-26 13:10:25 -0700785
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700786def LoadPartitionFiles(z, partition):
787 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700788 ZipFile, and return a dict of {filename: File object}."""
789 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700790 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700791 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700792 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700793 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700794 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700795 data = z.read(info.filename)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +0900796 out[fn] = common.File(fn, data, info.compress_size)
Doug Zongker1807e702012-02-28 12:21:08 -0800797 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700798
799
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700800def GetBuildProp(prop, info_dict):
801 """Return the fingerprint of the build of a given target-files info_dict."""
802 try:
803 return info_dict.get("build.prop", {})[prop]
804 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700805 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700806
Doug Zongkerfc44a512014-08-26 13:10:25 -0700807
Michael Runge4038aa82013-12-13 18:06:28 -0800808def AddToKnownPaths(filename, known_paths):
809 if filename[-1] == "/":
810 return
811 dirs = filename.split("/")[:-1]
812 while len(dirs) > 0:
813 path = "/".join(dirs)
814 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700815 break
Michael Runge4038aa82013-12-13 18:06:28 -0800816 known_paths.add(path)
817 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700818
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700819
Geremy Condra36bd3652014-02-06 19:45:10 -0800820def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700821 # TODO(tbao): We should factor out the common parts between
822 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800823 source_version = OPTIONS.source_info_dict["recovery_api_version"]
824 target_version = OPTIONS.target_info_dict["recovery_api_version"]
825
826 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700827 print("WARNING: generating edify script for a source that "
828 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700829 script = edify_generator.EdifyGenerator(
830 source_version, OPTIONS.target_info_dict,
831 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800832
Tao Bao3806c232015-07-05 21:08:33 -0700833 recovery_mount_options = OPTIONS.source_info_dict.get(
834 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700835 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
836 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Tao Bao3806c232015-07-05 21:08:33 -0700837 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700838 if source_oem_props or target_oem_props:
Tao Bao3806c232015-07-05 21:08:33 -0700839 if OPTIONS.oem_source is None:
840 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800841 if not OPTIONS.oem_no_mount:
842 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700843 oem_dict = common.LoadDictionaryFromLines(
844 open(OPTIONS.oem_source).readlines())
845
Dan Albert8b72aef2015-03-23 19:13:21 -0700846 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700847 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
848 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800849 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700850 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800851
Tao Bao5d182562016-02-23 11:38:39 -0800852 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
853 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
854 is_downgrade = long(post_timestamp) < long(pre_timestamp)
855
856 if OPTIONS.downgrade:
857 metadata["ota-downgrade"] = "yes"
858 if not is_downgrade:
859 raise RuntimeError("--downgrade specified but no downgrade detected: "
860 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
861 else:
862 if is_downgrade:
863 # Non-fatal here to allow generating such a package which may require
864 # manual work to adjust the post-timestamp. A legit use case is that we
865 # cut a new build C (after having A and B), but want to enfore the
866 # update path of A -> C -> B. Specifying --downgrade may not help since
867 # that would enforce a data wipe for C -> B update.
868 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
869 "The package may not be deployed properly. "
870 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
871 metadata["post-timestamp"] = post_timestamp
872
Geremy Condra36bd3652014-02-06 19:45:10 -0800873 device_specific = common.DeviceSpecificParams(
874 source_zip=source_zip,
875 source_version=source_version,
876 target_zip=target_zip,
877 target_version=target_version,
878 output_zip=output_zip,
879 script=script,
880 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700881 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800882
Tao Bao3e30d972016-03-15 13:20:19 -0700883 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700884 OPTIONS.source_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -0700885 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700886 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800887 metadata["pre-build"] = source_fp
888 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700889 metadata["pre-build-incremental"] = GetBuildProp(
890 "ro.build.version.incremental", OPTIONS.source_info_dict)
891 metadata["post-build-incremental"] = GetBuildProp(
892 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800893
894 source_boot = common.GetBootableImage(
895 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
896 OPTIONS.source_info_dict)
897 target_boot = common.GetBootableImage(
898 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
899 updating_boot = (not OPTIONS.two_step and
900 (source_boot.data != target_boot.data))
901
Geremy Condra36bd3652014-02-06 19:45:10 -0800902 target_recovery = common.GetBootableImage(
903 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800904
Doug Zongkerfc44a512014-08-26 13:10:25 -0700905 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
906 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700907
908 blockimgdiff_version = 1
909 if OPTIONS.info_dict:
910 blockimgdiff_version = max(
911 int(i) for i in
912 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
913
Tao Baof8acad12016-07-07 09:09:58 -0700914 # Check the first block of the source system partition for remount R/W only
915 # if the filesystem is ext4.
916 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
917 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700918 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
919 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
920 # b) the blocks listed in block map may not contain all the bytes for a given
921 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700922 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
923 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
924 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700925 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800926 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700927 version=blockimgdiff_version,
928 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700929
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700930 if HasVendorPartition(target_zip):
931 if not HasVendorPartition(source_zip):
932 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700933 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
934 OPTIONS.source_info_dict)
935 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
936 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800937
938 # Check first block of vendor partition for remount R/W only if
939 # disk type is ext4
940 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800941 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700942 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700943 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800944 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700945 version=blockimgdiff_version,
946 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700947 else:
948 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800949
Michael Rungec6e3afd2014-05-05 11:55:47 -0700950 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800951 device_specific.IncrementalOTA_Assertions()
952
953 # Two-step incremental package strategy (in chronological order,
954 # which is *not* the order in which the generated script has
955 # things):
956 #
957 # if stage is not "2/3" or "3/3":
958 # do verification on current system
959 # write recovery image to boot partition
960 # set stage to "2/3"
961 # reboot to boot partition and restart recovery
962 # else if stage is "2/3":
963 # write recovery image to recovery partition
964 # set stage to "3/3"
965 # reboot to recovery partition and restart recovery
966 # else:
967 # (stage must be "3/3")
968 # perform update:
969 # patch system files, etc.
970 # force full install of new boot image
971 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700972 # complete script normally
973 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800974
975 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700976 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800977 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700978 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800979 assert fs.fs_type.upper() == "EMMC", \
980 "two-step packages only supported on devices with EMMC /misc partitions"
981 bcb_dev = {"bcb_dev": fs.device}
982 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
983 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700984if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800985""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800986
987 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
988 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700989 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800990 script.WriteRawImage("/recovery", "recovery.img")
991 script.AppendExtra("""
992set_stage("%(bcb_dev)s", "3/3");
993reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700994else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800995""" % bcb_dev)
996
Tao Baod42e97e2016-11-30 12:11:57 -0800997 # Stage 1/3: (a) Verify the current system.
998 script.Comment("Stage 1/3")
999
Tao Bao6c55a8a2015-04-08 15:30:27 -07001000 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -08001001 script.Print("Source: %s" % (source_fp,))
1002 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001003
Geremy Condra36bd3652014-02-06 19:45:10 -08001004 script.Print("Verifying current system...")
1005
1006 device_specific.IncrementalOTA_VerifyBegin()
1007
Tao Bao3e30d972016-03-15 13:20:19 -07001008 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1009 # patching on a device that's already on the target build will damage the
1010 # system. Because operations like move don't check the block state, they
1011 # always apply the changes unconditionally.
1012 if blockimgdiff_version <= 2:
1013 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001014 script.AssertSomeFingerprint(source_fp)
1015 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001016 script.AssertSomeThumbprint(
1017 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001018
1019 else: # blockimgdiff_version > 2
1020 if source_oem_props is None and target_oem_props is None:
1021 script.AssertSomeFingerprint(source_fp, target_fp)
1022 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001023 script.AssertSomeThumbprint(
1024 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1025 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001026 elif source_oem_props is None and target_oem_props is not None:
1027 script.AssertFingerprintOrThumbprint(
1028 source_fp,
1029 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1030 else:
1031 script.AssertFingerprintOrThumbprint(
1032 target_fp,
1033 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001034
Tao Baod8d14be2016-02-04 14:26:02 -08001035 # Check the required cache size (i.e. stashed blocks).
1036 size = []
1037 if system_diff:
1038 size.append(system_diff.required_cache)
1039 if vendor_diff:
1040 size.append(vendor_diff.required_cache)
1041
Geremy Condra36bd3652014-02-06 19:45:10 -08001042 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001043 boot_type, boot_device = common.GetTypeAndDevice(
1044 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001045 d = common.Difference(target_boot, source_boot)
1046 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001047 if d is None:
1048 include_full_boot = True
1049 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1050 else:
1051 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001052
Tao Bao89fbb0f2017-01-10 10:47:58 -08001053 print("boot target: %d source: %d diff: %d" % (
1054 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001055
Doug Zongkerf8340082014-08-05 10:39:37 -07001056 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001057
Doug Zongkerf8340082014-08-05 10:39:37 -07001058 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1059 (boot_type, boot_device,
1060 source_boot.size, source_boot.sha1,
1061 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001062 size.append(target_boot.size)
1063
1064 if size:
1065 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001066
1067 device_specific.IncrementalOTA_VerifyEnd()
1068
1069 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001070 # Stage 1/3: (b) Write recovery image to /boot.
1071 _WriteRecoveryImageToBoot(script, output_zip)
1072
Geremy Condra36bd3652014-02-06 19:45:10 -08001073 script.AppendExtra("""
1074set_stage("%(bcb_dev)s", "2/3");
1075reboot_now("%(bcb_dev)s", "");
1076else
1077""" % bcb_dev)
1078
Tao Baod42e97e2016-11-30 12:11:57 -08001079 # Stage 3/3: Make changes.
1080 script.Comment("Stage 3/3")
1081
Jesse Zhao75bcea02015-01-06 10:59:53 -08001082 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001083 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001084 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001085 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001086
Geremy Condra36bd3652014-02-06 19:45:10 -08001087 script.Comment("---- start making changes here ----")
1088
1089 device_specific.IncrementalOTA_InstallBegin()
1090
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001091 system_diff.WriteScript(script, output_zip,
1092 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001093
Doug Zongkerfc44a512014-08-26 13:10:25 -07001094 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001095 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001096
1097 if OPTIONS.two_step:
1098 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1099 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001100 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001101
1102 if not OPTIONS.two_step:
1103 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001104 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001105 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001106 script.Print("Installing boot image...")
1107 script.WriteRawImage("/boot", "boot.img")
1108 else:
1109 # Produce the boot image by applying a patch to the current
1110 # contents of the boot partition, and write it back to the
1111 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001112 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001113 script.Print("Patching boot image...")
1114 script.ShowProgress(0.1, 10)
1115 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1116 % (boot_type, boot_device,
1117 source_boot.size, source_boot.sha1,
1118 target_boot.size, target_boot.sha1),
1119 "-",
1120 target_boot.size, target_boot.sha1,
1121 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001122 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001123 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001124
1125 # Do device-specific installation (eg, write radio image).
1126 device_specific.IncrementalOTA_InstallEnd()
1127
1128 if OPTIONS.extra_script is not None:
1129 script.AppendExtra(OPTIONS.extra_script)
1130
Doug Zongker922206e2014-03-04 13:16:24 -08001131 if OPTIONS.wipe_user_data:
1132 script.Print("Erasing user data...")
1133 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001134 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001135
Geremy Condra36bd3652014-02-06 19:45:10 -08001136 if OPTIONS.two_step:
1137 script.AppendExtra("""
1138set_stage("%(bcb_dev)s", "");
1139endif;
1140endif;
1141""" % bcb_dev)
1142
1143 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001144 # For downgrade OTAs, we prefer to use the update-binary in the source
1145 # build that is actually newer than the one in the target build.
1146 if OPTIONS.downgrade:
1147 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1148 else:
1149 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001150 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001151 WriteMetadata(metadata, output_zip)
1152
Doug Zongker32b527d2014-03-04 10:03:02 -08001153
Tao Bao9bc6bb22015-11-09 16:58:28 -08001154def WriteVerifyPackage(input_zip, output_zip):
1155 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1156
1157 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1158 recovery_mount_options = OPTIONS.info_dict.get(
1159 "recovery_mount_options")
1160 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001161 if oem_props:
Tao Bao9bc6bb22015-11-09 16:58:28 -08001162 if OPTIONS.oem_source is None:
1163 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -08001164 if not OPTIONS.oem_no_mount:
1165 script.Mount("/oem", recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001166 oem_dict = common.LoadDictionaryFromLines(
1167 open(OPTIONS.oem_source).readlines())
1168
1169 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1170 metadata = {
1171 "post-build": target_fp,
1172 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1173 OPTIONS.info_dict),
1174 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1175 }
1176
1177 device_specific = common.DeviceSpecificParams(
1178 input_zip=input_zip,
1179 input_version=OPTIONS.info_dict["recovery_api_version"],
1180 output_zip=output_zip,
1181 script=script,
1182 input_tmp=OPTIONS.input_tmp,
1183 metadata=metadata,
1184 info_dict=OPTIONS.info_dict)
1185
1186 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1187
1188 script.Print("Verifying device images against %s..." % target_fp)
1189 script.AppendExtra("")
1190
1191 script.Print("Verifying boot...")
1192 boot_img = common.GetBootableImage(
1193 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1194 boot_type, boot_device = common.GetTypeAndDevice(
1195 "/boot", OPTIONS.info_dict)
1196 script.Verify("%s:%s:%d:%s" % (
1197 boot_type, boot_device, boot_img.size, boot_img.sha1))
1198 script.AppendExtra("")
1199
1200 script.Print("Verifying recovery...")
1201 recovery_img = common.GetBootableImage(
1202 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1203 recovery_type, recovery_device = common.GetTypeAndDevice(
1204 "/recovery", OPTIONS.info_dict)
1205 script.Verify("%s:%s:%d:%s" % (
1206 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1207 script.AppendExtra("")
1208
1209 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1210 system_tgt.ResetFileMap()
1211 system_diff = common.BlockDifference("system", system_tgt, src=None)
1212 system_diff.WriteStrictVerifyScript(script)
1213
1214 if HasVendorPartition(input_zip):
1215 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1216 vendor_tgt.ResetFileMap()
1217 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1218 vendor_diff.WriteStrictVerifyScript(script)
1219
1220 # Device specific partitions, such as radio, bootloader and etc.
1221 device_specific.VerifyOTA_Assertions()
1222
1223 script.SetProgress(1.0)
1224 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001225 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001226 WriteMetadata(metadata, output_zip)
1227
1228
Tao Baoc098e9e2016-01-07 13:03:56 -08001229def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1230 source_file=None):
1231 """Generate an Android OTA package that has A/B update payload."""
1232
Alex Deymod8d96ec2016-06-10 16:38:31 -07001233 # The place where the output from the subprocess should go.
1234 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1235
Tao Baoc098e9e2016-01-07 13:03:56 -08001236 # Setup signing keys.
1237 if OPTIONS.package_key is None:
1238 OPTIONS.package_key = OPTIONS.info_dict.get(
1239 "default_system_dev_certificate",
1240 "build/target/product/security/testkey")
1241
Tao Baodea0f8b2016-06-20 17:55:06 -07001242 # A/B updater expects a signing key in RSA format. Gets the key ready for
1243 # later use in step 3, unless a payload_signer has been specified.
1244 if OPTIONS.payload_signer is None:
1245 cmd = ["openssl", "pkcs8",
1246 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1247 "-inform", "DER", "-nocrypt"]
1248 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1249 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001250 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1251 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001252 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001253
Tao Baodea0f8b2016-06-20 17:55:06 -07001254 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001255 temp_zip_file = tempfile.NamedTemporaryFile()
1256 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1257 compression=zipfile.ZIP_DEFLATED)
1258
1259 # Metadata to comply with Android OTA package format.
1260 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1261 oem_dict = None
1262 if oem_props:
1263 if OPTIONS.oem_source is None:
1264 raise common.ExternalError("OEM source required for this build")
1265 oem_dict = common.LoadDictionaryFromLines(
1266 open(OPTIONS.oem_source).readlines())
1267
1268 metadata = {
1269 "post-build": CalculateFingerprint(oem_props, oem_dict,
1270 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001271 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1272 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001273 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1274 OPTIONS.info_dict),
1275 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001276 "ota-required-cache": "0",
1277 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001278 }
1279
1280 if source_file is not None:
1281 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1282 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001283 metadata["pre-build-incremental"] = GetBuildProp(
1284 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001285
1286 # 1. Generate payload.
1287 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1288 cmd = ["brillo_update_payload", "generate",
1289 "--payload", payload_file,
1290 "--target_image", target_file]
1291 if source_file is not None:
1292 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001293 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1294 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001295 assert p1.returncode == 0, "brillo_update_payload generate failed"
1296
1297 # 2. Generate hashes of the payload and metadata files.
1298 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1299 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1300 cmd = ["brillo_update_payload", "hash",
1301 "--unsigned_payload", payload_file,
1302 "--signature_size", "256",
1303 "--metadata_hash_file", metadata_sig_file,
1304 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001305 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1306 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001307 assert p1.returncode == 0, "brillo_update_payload hash failed"
1308
1309 # 3. Sign the hashes and insert them back into the payload file.
1310 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1311 suffix=".bin")
1312 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1313 suffix=".bin")
1314 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001315 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001316 cmd = [OPTIONS.payload_signer]
1317 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001318 else:
1319 cmd = ["openssl", "pkeyutl", "-sign",
1320 "-inkey", rsa_key,
1321 "-pkeyopt", "digest:sha256"]
1322 cmd.extend(["-in", payload_sig_file,
1323 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001324 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1325 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001326 assert p1.returncode == 0, "openssl sign payload failed"
1327
1328 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001329 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001330 cmd = [OPTIONS.payload_signer]
1331 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001332 else:
1333 cmd = ["openssl", "pkeyutl", "-sign",
1334 "-inkey", rsa_key,
1335 "-pkeyopt", "digest:sha256"]
1336 cmd.extend(["-in", metadata_sig_file,
1337 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001338 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1339 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001340 assert p1.returncode == 0, "openssl sign metadata failed"
1341
1342 # 3c. Insert the signatures back into the payload file.
1343 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1344 suffix=".bin")
1345 cmd = ["brillo_update_payload", "sign",
1346 "--unsigned_payload", payload_file,
1347 "--payload", signed_payload_file,
1348 "--signature_size", "256",
1349 "--metadata_signature_file", signed_metadata_sig_file,
1350 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001351 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1352 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001353 assert p1.returncode == 0, "brillo_update_payload sign failed"
1354
Alex Deymo19241c12016-02-04 22:29:29 -08001355 # 4. Dump the signed payload properties.
1356 properties_file = common.MakeTempFile(prefix="payload-properties-",
1357 suffix=".txt")
1358 cmd = ["brillo_update_payload", "properties",
1359 "--payload", signed_payload_file,
1360 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001361 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1362 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001363 assert p1.returncode == 0, "brillo_update_payload properties failed"
1364
Tao Bao7c5dc572016-06-14 17:48:11 -07001365 if OPTIONS.wipe_user_data:
1366 with open(properties_file, "a") as f:
1367 f.write("POWERWASH=1\n")
1368 metadata["ota-wipe"] = "yes"
1369
Alex Deymo19241c12016-02-04 22:29:29 -08001370 # Add the signed payload file and properties into the zip.
1371 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001372 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1373 compress_type=zipfile.ZIP_STORED)
1374 WriteMetadata(metadata, output_zip)
1375
Tianjie Xucfa86222016-03-07 16:31:19 -08001376 # If dm-verity is supported for the device, copy contents of care_map
1377 # into A/B OTA package.
1378 if OPTIONS.info_dict.get("verity") == "true":
1379 target_zip = zipfile.ZipFile(target_file, "r")
1380 care_map_path = "META/care_map.txt"
1381 namelist = target_zip.namelist()
1382 if care_map_path in namelist:
1383 care_map_data = target_zip.read(care_map_path)
1384 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data)
1385 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001386 print("Warning: cannot find care map file in target_file package")
Tianjie Xucfa86222016-03-07 16:31:19 -08001387 common.ZipClose(target_zip)
1388
Tao Baoc098e9e2016-01-07 13:03:56 -08001389 # Sign the whole package to comply with the Android OTA package format.
1390 common.ZipClose(output_zip)
1391 SignOutput(temp_zip_file.name, output_file)
1392 temp_zip_file.close()
1393
1394
Dan Albert8b72aef2015-03-23 19:13:21 -07001395class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001396 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001397 self.deferred_patch_list = None
Tao Bao89fbb0f2017-01-10 10:47:58 -08001398 print("Loading target...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001399 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001400 print("Loading source...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001401 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1402
1403 self.verbatim_targets = verbatim_targets = []
1404 self.patch_list = patch_list = []
1405 diffs = []
1406 self.renames = renames = {}
1407 known_paths = set()
1408 largest_source_size = 0
1409
1410 matching_file_cache = {}
1411 for fn, sf in source_data.items():
1412 assert fn == sf.name
1413 matching_file_cache["path:" + fn] = sf
1414 if fn in target_data.keys():
1415 AddToKnownPaths(fn, known_paths)
1416 # Only allow eligibility for filename/sha matching
1417 # if there isn't a perfect path match.
1418 if target_data.get(sf.name) is None:
1419 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1420 matching_file_cache["sha:" + sf.sha1] = sf
1421
1422 for fn in sorted(target_data.keys()):
1423 tf = target_data[fn]
1424 assert fn == tf.name
1425 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1426 if sf is not None and sf.name != tf.name:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001427 print("File has moved from " + sf.name + " to " + tf.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001428 renames[sf.name] = tf
1429
1430 if sf is None or fn in OPTIONS.require_verbatim:
1431 # This file should be included verbatim
1432 if fn in OPTIONS.prohibit_verbatim:
1433 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Tao Bao89fbb0f2017-01-10 10:47:58 -08001434 print("send", fn, "verbatim")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001435 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001436 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001437 if fn in target_data.keys():
1438 AddToKnownPaths(fn, known_paths)
1439 elif tf.sha1 != sf.sha1:
1440 # File is different; consider sending as a patch
1441 diffs.append(common.Difference(tf, sf))
1442 else:
1443 # Target file data identical to source (may still be renamed)
1444 pass
1445
1446 common.ComputeDifferences(diffs)
1447
1448 for diff in diffs:
1449 tf, sf, d = diff.GetPatch()
1450 path = "/".join(tf.name.split("/")[:-1])
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001451 if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001452 path not in known_paths:
1453 # patch is almost as big as the file; don't bother patching
1454 # or a patch + rename cannot take place due to the target
1455 # directory not existing
1456 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001457 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001458 if sf.name in renames:
1459 del renames[sf.name]
1460 AddToKnownPaths(tf.name, known_paths)
1461 else:
1462 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1463 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1464 largest_source_size = max(largest_source_size, sf.size)
1465
1466 self.largest_source_size = largest_source_size
1467
1468 def EmitVerification(self, script):
1469 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001470 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001471 if tf.name != sf.name:
1472 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1473 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1474 so_far += sf.size
1475 return so_far
1476
Michael Runge63f01de2014-10-28 19:24:19 -07001477 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001478 for fn, _, sha1 in self.verbatim_targets:
1479 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001480 script.FileCheck("/"+fn, sha1)
1481 for tf, _, _, _ in self.patch_list:
1482 script.FileCheck(tf.name, tf.sha1)
1483
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001484 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001485 file_list = ["/" + i[0] for i in self.verbatim_targets]
1486 file_list += ["/" + i for i in self.source_data
1487 if i not in self.target_data and i not in self.renames]
1488 file_list += list(extras)
1489 # Sort the list in descending order, which removes all the files first
1490 # before attempting to remove the folder. (Bug: 22960996)
1491 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001492
1493 def TotalPatchSize(self):
1494 return sum(i[1].size for i in self.patch_list)
1495
1496 def EmitPatches(self, script, total_patch_size, so_far):
1497 self.deferred_patch_list = deferred_patch_list = []
1498 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001499 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001500 if tf.name == "system/build.prop":
1501 deferred_patch_list.append(item)
1502 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001503 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001504 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001505 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1506 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001507 so_far += tf.size
1508 script.SetProgress(so_far / total_patch_size)
1509 return so_far
1510
1511 def EmitDeferredPatches(self, script):
1512 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001513 tf, sf, _, _ = item
1514 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1515 "patch/" + sf.name + ".p")
1516 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001517
1518 def EmitRenames(self, script):
1519 if len(self.renames) > 0:
1520 script.Print("Renaming files...")
1521 for src, tgt in self.renames.iteritems():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001522 print("Renaming " + src + " to " + tgt.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001523 script.RenameFile(src, tgt.name)
1524
1525
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001526def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001527 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1528 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1529
Doug Zongker26e66192014-02-20 13:22:07 -08001530 if (OPTIONS.block_based and
1531 target_has_recovery_patch and
1532 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001533 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1534
Doug Zongker37974732010-09-16 17:44:38 -07001535 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1536 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001537
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001538 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001539 print("WARNING: generating edify script for a source that "
1540 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001541 script = edify_generator.EdifyGenerator(
1542 source_version, OPTIONS.target_info_dict,
1543 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001544
Tao Bao34b47bf2015-06-22 19:17:41 -07001545 recovery_mount_options = OPTIONS.source_info_dict.get(
1546 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001547 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1548 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -07001549 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001550 if source_oem_props or target_oem_props:
Michael Runge6e836112014-04-15 17:40:21 -07001551 if OPTIONS.oem_source is None:
1552 raise common.ExternalError("OEM source required for this build")
Tao Bao1bb5a182016-03-04 09:45:03 -08001553 if not OPTIONS.oem_no_mount:
1554 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001555 oem_dict = common.LoadDictionaryFromLines(
1556 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001557
Dan Albert8b72aef2015-03-23 19:13:21 -07001558 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001559 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
1560 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001561 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001562 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001563
Tao Bao5d182562016-02-23 11:38:39 -08001564 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
1565 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1566 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1567
1568 if OPTIONS.downgrade:
1569 metadata["ota-downgrade"] = "yes"
1570 if not is_downgrade:
1571 raise RuntimeError("--downgrade specified but no downgrade detected: "
1572 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
1573 else:
1574 if is_downgrade:
1575 # Non-fatal here to allow generating such a package which may require
1576 # manual work to adjust the post-timestamp. A legit use case is that we
1577 # cut a new build C (after having A and B), but want to enfore the
1578 # update path of A -> C -> B. Specifying --downgrade may not help since
1579 # that would enforce a data wipe for C -> B update.
1580 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
1581 "The package may not be deployed properly. "
1582 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
1583 metadata["post-timestamp"] = post_timestamp
1584
Doug Zongker05d3dea2009-06-22 11:32:31 -07001585 device_specific = common.DeviceSpecificParams(
1586 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001587 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001588 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001589 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001590 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001591 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001592 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001593 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001594
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001595 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001596 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001597 if HasVendorPartition(target_zip):
1598 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001599 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001600 else:
1601 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001602
Tao Bao3e30d972016-03-15 13:20:19 -07001603 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001604 OPTIONS.target_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -07001605 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001606 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001607
Tao Bao3e30d972016-03-15 13:20:19 -07001608 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001609 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001610 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001611 script.AssertSomeThumbprint(
1612 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1613 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001614 elif source_oem_props is None and target_oem_props is not None:
1615 script.AssertFingerprintOrThumbprint(
1616 source_fp,
1617 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1618 else:
1619 script.AssertFingerprintOrThumbprint(
1620 target_fp,
1621 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001622
Doug Zongker2ea21062010-04-28 16:05:21 -07001623 metadata["pre-build"] = source_fp
1624 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001625 metadata["pre-build-incremental"] = GetBuildProp(
1626 "ro.build.version.incremental", OPTIONS.source_info_dict)
1627 metadata["post-build-incremental"] = GetBuildProp(
1628 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001629
Doug Zongker55d93282011-01-25 17:03:34 -08001630 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001631 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1632 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001633 target_boot = common.GetBootableImage(
1634 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001635 updating_boot = (not OPTIONS.two_step and
1636 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001637
Doug Zongker55d93282011-01-25 17:03:34 -08001638 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001639 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1640 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001641 target_recovery = common.GetBootableImage(
1642 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001643 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001644
Doug Zongker881dd402009-09-20 14:03:55 -07001645 # Here's how we divide up the progress bar:
1646 # 0.1 for verifying the start state (PatchCheck calls)
1647 # 0.8 for applying patches (ApplyPatch calls)
1648 # 0.1 for unpacking verbatim files, symlinking, and doing the
1649 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001650
Michael Runge6e836112014-04-15 17:40:21 -07001651 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001652 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001653
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001654 # Two-step incremental package strategy (in chronological order,
1655 # which is *not* the order in which the generated script has
1656 # things):
1657 #
1658 # if stage is not "2/3" or "3/3":
1659 # do verification on current system
1660 # write recovery image to boot partition
1661 # set stage to "2/3"
1662 # reboot to boot partition and restart recovery
1663 # else if stage is "2/3":
1664 # write recovery image to recovery partition
1665 # set stage to "3/3"
1666 # reboot to recovery partition and restart recovery
1667 # else:
1668 # (stage must be "3/3")
1669 # perform update:
1670 # patch system files, etc.
1671 # force full install of new boot image
1672 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001673 # complete script normally
1674 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001675
1676 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001677 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001678 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001679 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001680 assert fs.fs_type.upper() == "EMMC", \
1681 "two-step packages only supported on devices with EMMC /misc partitions"
1682 bcb_dev = {"bcb_dev": fs.device}
1683 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1684 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001685if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001686""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001687
1688 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1689 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001690 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001691 script.WriteRawImage("/recovery", "recovery.img")
1692 script.AppendExtra("""
1693set_stage("%(bcb_dev)s", "3/3");
1694reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001695else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001696""" % bcb_dev)
1697
Tao Baod42e97e2016-11-30 12:11:57 -08001698 # Stage 1/3: (a) Verify the current system.
1699 script.Comment("Stage 1/3")
1700
Tao Bao6c55a8a2015-04-08 15:30:27 -07001701 # Dump fingerprints
1702 script.Print("Source: %s" % (source_fp,))
1703 script.Print("Target: %s" % (target_fp,))
1704
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001705 script.Print("Verifying current system...")
1706
Doug Zongkere5ff5902012-01-17 10:55:37 -08001707 device_specific.IncrementalOTA_VerifyBegin()
1708
Doug Zongker881dd402009-09-20 14:03:55 -07001709 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001710 so_far = system_diff.EmitVerification(script)
1711 if vendor_diff:
1712 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001713
Tao Baod8d14be2016-02-04 14:26:02 -08001714 size = []
1715 if system_diff.patch_list:
1716 size.append(system_diff.largest_source_size)
1717 if vendor_diff:
1718 if vendor_diff.patch_list:
1719 size.append(vendor_diff.largest_source_size)
1720
Doug Zongker5da317e2009-06-02 13:38:17 -07001721 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001722 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001723 _, _, d = d.ComputePatch()
Tao Bao89fbb0f2017-01-10 10:47:58 -08001724 print("boot target: %d source: %d diff: %d" % (
1725 target_boot.size, source_boot.size, len(d)))
Doug Zongker5da317e2009-06-02 13:38:17 -07001726
Doug Zongker048e7ca2009-06-15 14:31:53 -07001727 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001728
Tao Baodd24da92015-07-29 14:09:23 -07001729 boot_type, boot_device = common.GetTypeAndDevice(
1730 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001731
1732 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1733 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001734 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001735 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001736 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001737 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001738
Tao Baod8d14be2016-02-04 14:26:02 -08001739 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001740 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001741
Doug Zongker05d3dea2009-06-22 11:32:31 -07001742 device_specific.IncrementalOTA_VerifyEnd()
1743
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001744 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001745 # Stage 1/3: (b) Write recovery image to /boot.
1746 _WriteRecoveryImageToBoot(script, output_zip)
1747
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001748 script.AppendExtra("""
1749set_stage("%(bcb_dev)s", "2/3");
1750reboot_now("%(bcb_dev)s", "");
1751else
1752""" % bcb_dev)
1753
Tao Baod42e97e2016-11-30 12:11:57 -08001754 # Stage 3/3: Make changes.
1755 script.Comment("Stage 3/3")
1756
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001757 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001758
Doug Zongkere5ff5902012-01-17 10:55:37 -08001759 device_specific.IncrementalOTA_InstallBegin()
1760
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001761 if OPTIONS.two_step:
1762 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1763 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001764 print("writing full boot image (forced by two-step mode)")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001765
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001766 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001767 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1768 if vendor_diff:
1769 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001770
Doug Zongker881dd402009-09-20 14:03:55 -07001771 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001772 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1773 if vendor_diff:
1774 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001775 if updating_boot:
1776 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001777
1778 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001779 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1780 if vendor_diff:
1781 script.Print("Patching vendor files...")
1782 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001783
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001784 if not OPTIONS.two_step:
1785 if updating_boot:
1786 # Produce the boot image by applying a patch to the current
1787 # contents of the boot partition, and write it back to the
1788 # partition.
1789 script.Print("Patching boot image...")
1790 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1791 % (boot_type, boot_device,
1792 source_boot.size, source_boot.sha1,
1793 target_boot.size, target_boot.sha1),
1794 "-",
1795 target_boot.size, target_boot.sha1,
1796 source_boot.sha1, "patch/boot.img.p")
1797 so_far += target_boot.size
1798 script.SetProgress(so_far / total_patch_size)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001799 print("boot image changed; including.")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001800 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001801 print("boot image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001802
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001803 system_items = ItemSet("system", "META/filesystem_config.txt")
1804 if vendor_diff:
1805 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1806
Doug Zongkereef39442009-04-02 12:14:19 -07001807 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001808 # Recovery is generated as a patch using both the boot image
1809 # (which contains the same linux kernel as recovery) and the file
1810 # /system/etc/recovery-resource.dat (which contains all the images
1811 # used in the recovery UI) as sources. This lets us minimize the
1812 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001813 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001814 # For older builds where recovery-resource.dat is not present, we
1815 # use only the boot image as the source.
1816
Doug Zongkerc9253822014-02-04 12:17:58 -08001817 if not target_has_recovery_patch:
1818 def output_sink(fn, data):
1819 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001820 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001821
1822 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1823 target_recovery, target_boot)
1824 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001825 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001826 "/system/etc/install-recovery.sh"])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001827 print("recovery image changed; including as patch from boot.")
Doug Zongkereef39442009-04-02 12:14:19 -07001828 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001829 print("recovery image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001830
Doug Zongker881dd402009-09-20 14:03:55 -07001831 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001832
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001833 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1834 if vendor_diff:
1835 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1836
1837 temp_script = script.MakeTemporary()
1838 system_items.GetMetadata(target_zip)
1839 system_items.Get("system").SetPermissions(temp_script)
1840 if vendor_diff:
1841 vendor_items.GetMetadata(target_zip)
1842 vendor_items.Get("vendor").SetPermissions(temp_script)
1843
1844 # Note that this call will mess up the trees of Items, so make sure
1845 # we're done with them.
1846 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1847 if vendor_diff:
1848 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001849
1850 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001851 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1852
1853 # Delete all the symlinks in source that aren't in target. This
1854 # needs to happen before verbatim files are unpacked, in case a
1855 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001856
1857 # If a symlink in the source will be replaced by a regular file, we cannot
1858 # delete the symlink/file in case the package gets applied again. For such
1859 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1860 # (Bug: 23646151)
1861 replaced_symlinks = dict()
1862 if system_diff:
1863 for i in system_diff.verbatim_targets:
1864 replaced_symlinks["/%s" % (i[0],)] = i[2]
1865 if vendor_diff:
1866 for i in vendor_diff.verbatim_targets:
1867 replaced_symlinks["/%s" % (i[0],)] = i[2]
1868
1869 if system_diff:
1870 for tf in system_diff.renames.values():
1871 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1872 if vendor_diff:
1873 for tf in vendor_diff.renames.values():
1874 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1875
1876 always_delete = []
1877 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001878 for dest, link in source_symlinks:
1879 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001880 if link in replaced_symlinks:
1881 may_delete.append((link, replaced_symlinks[link]))
1882 else:
1883 always_delete.append(link)
1884 script.DeleteFiles(always_delete)
1885 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001886
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001887 if system_diff.verbatim_targets:
1888 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001889 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001890 if vendor_diff and vendor_diff.verbatim_targets:
1891 script.Print("Unpacking new vendor files...")
1892 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001893
Doug Zongkerc9253822014-02-04 12:17:58 -08001894 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001895 script.Print("Unpacking new recovery...")
1896 script.UnpackPackageDir("recovery", "/system")
1897
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001898 system_diff.EmitRenames(script)
1899 if vendor_diff:
1900 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001901
Doug Zongker05d3dea2009-06-22 11:32:31 -07001902 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001903
1904 # Create all the symlinks that don't already exist, or point to
1905 # somewhere different than what we want. Delete each symlink before
1906 # creating it, since the 'symlink' command won't overwrite.
1907 to_create = []
1908 for dest, link in target_symlinks:
1909 if link in source_symlinks_d:
1910 if dest != source_symlinks_d[link]:
1911 to_create.append((dest, link))
1912 else:
1913 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001914 script.DeleteFiles([i[1] for i in to_create])
1915 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001916
1917 # Now that the symlinks are created, we can set all the
1918 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001919 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001920
Doug Zongker881dd402009-09-20 14:03:55 -07001921 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001922 device_specific.IncrementalOTA_InstallEnd()
1923
Doug Zongker1c390a22009-05-14 19:06:36 -07001924 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001925 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001926
Doug Zongkere92f15a2011-08-26 13:46:40 -07001927 # Patch the build.prop file last, so if something fails but the
1928 # device can still come up, it appears to be the old build and will
1929 # get set the OTA package again to retry.
1930 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001931 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001932
Doug Zongker922206e2014-03-04 13:16:24 -08001933 if OPTIONS.wipe_user_data:
1934 script.Print("Erasing user data...")
1935 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001936 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001937
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001938 if OPTIONS.two_step:
1939 script.AppendExtra("""
1940set_stage("%(bcb_dev)s", "");
1941endif;
1942endif;
1943""" % bcb_dev)
1944
Michael Runge63f01de2014-10-28 19:24:19 -07001945 if OPTIONS.verify and system_diff:
1946 script.Print("Remounting and verifying system partition files...")
1947 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001948 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001949 system_diff.EmitExplicitTargetVerification(script)
1950
1951 if OPTIONS.verify and vendor_diff:
1952 script.Print("Remounting and verifying vendor partition files...")
1953 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001954 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001955 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08001956
1957 # For downgrade OTAs, we prefer to use the update-binary in the source
1958 # build that is actually newer than the one in the target build.
1959 if OPTIONS.downgrade:
1960 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1961 else:
1962 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001963
Tao Baod8d14be2016-02-04 14:26:02 -08001964 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001965 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001966
1967
1968def main(argv):
1969
1970 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001971 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001972 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001973 elif o in ("-k", "--package_key"):
1974 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001975 elif o in ("-i", "--incremental_from"):
1976 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001977 elif o == "--full_radio":
1978 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001979 elif o == "--full_bootloader":
1980 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001981 elif o in ("-w", "--wipe_user_data"):
1982 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001983 elif o == "--downgrade":
1984 OPTIONS.downgrade = True
1985 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07001986 elif o in ("-o", "--oem_settings"):
1987 OPTIONS.oem_source = a
Tao Bao8608cde2016-02-25 19:49:55 -08001988 elif o == "--oem_no_mount":
1989 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001990 elif o in ("-e", "--extra_script"):
1991 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001992 elif o in ("-a", "--aslr_mode"):
1993 if a in ("on", "On", "true", "True", "yes", "Yes"):
1994 OPTIONS.aslr_mode = True
1995 else:
1996 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001997 elif o in ("-t", "--worker_threads"):
1998 if a.isdigit():
1999 OPTIONS.worker_threads = int(a)
2000 else:
2001 raise ValueError("Cannot parse value %r for option %r - only "
2002 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002003 elif o in ("-2", "--two_step"):
2004 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002005 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002006 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002007 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002008 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002009 elif o == "--block":
2010 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002011 elif o in ("-b", "--binary"):
2012 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002013 elif o in ("--no_fallback_to_full",):
2014 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002015 elif o == "--stash_threshold":
2016 try:
2017 OPTIONS.stash_threshold = float(a)
2018 except ValueError:
2019 raise ValueError("Cannot parse value %r for option %r - expecting "
2020 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002021 elif o == "--gen_verify":
2022 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002023 elif o == "--log_diff":
2024 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002025 elif o == "--payload_signer":
2026 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002027 elif o == "--payload_signer_args":
2028 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002029 else:
2030 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002031 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002032
2033 args = common.ParseOptions(argv, __doc__,
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002034 extra_opts="b:k:i:d:we:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002035 extra_long_opts=[
2036 "board_config=",
2037 "package_key=",
2038 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002039 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002040 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002041 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002042 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07002043 "extra_script=",
2044 "worker_threads=",
2045 "aslr_mode=",
2046 "two_step",
2047 "no_signing",
2048 "block",
2049 "binary=",
2050 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002051 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002052 "verify",
2053 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002054 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002055 "gen_verify",
2056 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002057 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002058 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002059 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002060
2061 if len(args) != 2:
2062 common.Usage(__doc__)
2063 sys.exit(1)
2064
Tao Bao5d182562016-02-23 11:38:39 -08002065 if OPTIONS.downgrade:
2066 # Sanity check to enforce a data wipe.
2067 if not OPTIONS.wipe_user_data:
2068 raise ValueError("Cannot downgrade without a data wipe")
2069
2070 # We should only allow downgrading incrementals (as opposed to full).
2071 # Otherwise the device may go back from arbitrary build with this full
2072 # OTA package.
2073 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002074 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002075
Tao Baoc098e9e2016-01-07 13:03:56 -08002076 # Load the dict file from the zip directly to have a peek at the OTA type.
2077 # For packages using A/B update, unzipping is not needed.
2078 input_zip = zipfile.ZipFile(args[0], "r")
2079 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2080 common.ZipClose(input_zip)
2081
2082 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2083
2084 if ab_update:
2085 if OPTIONS.incremental_source is not None:
2086 OPTIONS.target_info_dict = OPTIONS.info_dict
2087 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2088 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2089 common.ZipClose(source_zip)
2090
2091 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002092 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002093 common.DumpInfoDict(OPTIONS.info_dict)
2094
2095 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002096 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002097 common.DumpInfoDict(OPTIONS.source_info_dict)
2098
2099 WriteABOTAPackageWithBrilloScript(
2100 target_file=args[0],
2101 output_file=args[1],
2102 source_file=OPTIONS.incremental_source)
2103
Tao Bao89fbb0f2017-01-10 10:47:58 -08002104 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002105 return
2106
Doug Zongker1c390a22009-05-14 19:06:36 -07002107 if OPTIONS.extra_script is not None:
2108 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2109
Tao Bao89fbb0f2017-01-10 10:47:58 -08002110 print("unzipping target target-files...")
Doug Zongker55d93282011-01-25 17:03:34 -08002111 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002112
Doug Zongkereef39442009-04-02 12:14:19 -07002113 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002114 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002115
Doug Zongker37974732010-09-16 17:44:38 -07002116 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002117 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07002118 common.DumpInfoDict(OPTIONS.info_dict)
2119
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002120 # If the caller explicitly specified the device-specific extensions
2121 # path via -s/--device_specific, use that. Otherwise, use
2122 # META/releasetools.py if it is present in the target target_files.
2123 # Otherwise, take the path of the file from 'tool_extensions' in the
2124 # info dict and look for that in the local filesystem, relative to
2125 # the current directory.
2126
Doug Zongker37974732010-09-16 17:44:38 -07002127 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002128 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2129 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08002130 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002131 OPTIONS.device_specific = from_input
2132 else:
2133 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2134
Doug Zongker37974732010-09-16 17:44:38 -07002135 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002136 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002137
Tao Baoc098e9e2016-01-07 13:03:56 -08002138 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002139 raise common.ExternalError(
2140 "--- target build has specified no recovery ---")
2141
Tao Bao767e3ac2015-11-10 12:19:19 -08002142 # Use the default key to sign the package if not specified with package_key.
2143 if not OPTIONS.no_signing:
2144 if OPTIONS.package_key is None:
2145 OPTIONS.package_key = OPTIONS.info_dict.get(
2146 "default_system_dev_certificate",
2147 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002148
Tao Bao767e3ac2015-11-10 12:19:19 -08002149 # Set up the output zip. Create a temporary zip file if signing is needed.
2150 if OPTIONS.no_signing:
2151 if os.path.exists(args[1]):
2152 os.unlink(args[1])
2153 output_zip = zipfile.ZipFile(args[1], "w",
2154 compression=zipfile.ZIP_DEFLATED)
2155 else:
2156 temp_zip_file = tempfile.NamedTemporaryFile()
2157 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2158 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002159
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002160 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002161 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002162 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002163 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002164 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002165
Tao Bao9bc6bb22015-11-09 16:58:28 -08002166 # Generate a verify package.
2167 if OPTIONS.gen_verify:
2168 WriteVerifyPackage(input_zip, output_zip)
2169
Tao Bao767e3ac2015-11-10 12:19:19 -08002170 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002171 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002172 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002173
2174 # Generate an incremental OTA. It will fall back to generate a full OTA on
2175 # failure unless no_fallback_to_full is specified.
2176 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002177 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08002178 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2179 OPTIONS.incremental_source)
2180 OPTIONS.target_info_dict = OPTIONS.info_dict
2181 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2182 OPTIONS.source_tmp)
2183 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002184 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002185 common.DumpInfoDict(OPTIONS.source_info_dict)
2186 try:
2187 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002188 if OPTIONS.log_diff:
2189 out_file = open(OPTIONS.log_diff, 'w')
2190 import target_files_diff
2191 target_files_diff.recursiveDiff('',
2192 OPTIONS.source_tmp,
2193 OPTIONS.input_tmp,
2194 out_file)
2195 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002196 except ValueError:
2197 if not OPTIONS.fallback_to_full:
2198 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08002199 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002200 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002201 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002202
Tao Bao767e3ac2015-11-10 12:19:19 -08002203 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002204
Tao Bao767e3ac2015-11-10 12:19:19 -08002205 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002206 if not OPTIONS.no_signing:
2207 SignOutput(temp_zip_file.name, args[1])
2208 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002209
Tao Bao89fbb0f2017-01-10 10:47:58 -08002210 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002211
2212
2213if __name__ == '__main__':
2214 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002215 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002216 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002217 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002218 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002219 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002220 finally:
2221 common.Cleanup()