blob: bd033eb6d383ab4c0c70d15e403fd4202b3ecfc8 [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
Doug Zongker9b23f2c2013-11-25 14:44:12 -080078 -2 (--two_step)
79 Generate a 'two-step' OTA package, where recovery is updated
80 first, so that any changes made to the system partition are done
81 using the new recovery (new kernel, etc.).
82
Doug Zongker26e66192014-02-20 13:22:07 -080083 --block
84 Generate a block-based OTA if possible. Will fall back to a
85 file-based OTA if the target_files is older and doesn't support
86 block-based OTAs.
87
Doug Zongker25568482014-03-03 10:21:27 -080088 -b (--binary) <file>
89 Use the given binary as the update-binary in the output package,
90 instead of the binary in the build's target_files. Use for
91 development only.
92
Martin Blumenstingl374e1142014-05-31 20:42:55 +020093 -t (--worker_threads) <int>
94 Specifies the number of worker-threads that will be used when
95 generating patches for incremental updates (defaults to 3).
96
Tao Bao8dcf7382015-05-21 14:09:49 -070097 --stash_threshold <float>
98 Specifies the threshold that will be used to compute the maximum
99 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800100
101 --gen_verify
102 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800103
104 --log_diff <file>
105 Generate a log file that shows the differences in the source and target
106 builds for an incremental package. This option is only meaningful when
107 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700108
109 --payload_signer <signer>
110 Specify the signer when signing the payload and metadata for A/B OTAs.
111 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
112 with the package private key. If the private key cannot be accessed
113 directly, a payload signer that knows how to do that should be specified.
114 The signer will be supplied with "-inkey <path_to_key>",
115 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700116
117 --payload_signer_args <args>
118 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700119"""
120
Tao Bao89fbb0f2017-01-10 10:47:58 -0800121from __future__ import print_function
122
Doug Zongkereef39442009-04-02 12:14:19 -0700123import sys
124
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800125if sys.hexversion < 0x02070000:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800126 print("Python 2.7 or newer is required.", file=sys.stderr)
Doug Zongkereef39442009-04-02 12:14:19 -0700127 sys.exit(1)
128
Doug Zongkerfc44a512014-08-26 13:10:25 -0700129import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700130import os
Tao Baoc098e9e2016-01-07 13:03:56 -0800131import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700132import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700133import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700134import zipfile
135
136import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700137import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700138import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700139
140OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700141OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700142OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700143OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700144OPTIONS.require_verbatim = set()
145OPTIONS.prohibit_verbatim = set(("system/build.prop",))
146OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700147OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800148OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700149OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700150OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
151if OPTIONS.worker_threads == 0:
152 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800153OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900154OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800155OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800156OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700157OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800158OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700159OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700160OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700161OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700162# Stash size cannot exceed cache_size * threshold.
163OPTIONS.cache_size = None
164OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800165OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800166OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700167OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700168OPTIONS.payload_signer_args = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700169
Doug Zongkereef39442009-04-02 12:14:19 -0700170def MostPopularKey(d, default):
171 """Given a dict, return the key corresponding to the largest
172 value. Returns 'default' if the dict is empty."""
173 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700174 if not x:
175 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700176 x.sort()
177 return x[-1][1]
178
179
180def IsSymlink(info):
181 """Return true if the zipfile.ZipInfo object passed in represents a
182 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700183 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700184
Hristo Bojinov96be7202010-08-02 10:26:17 -0700185def IsRegular(info):
186 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700187 regular file."""
188 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700189
Michael Runge4038aa82013-12-13 18:06:28 -0800190def ClosestFileMatch(src, tgtfiles, existing):
191 """Returns the closest file match between a source file and list
192 of potential matches. The exact filename match is preferred,
193 then the sha1 is searched for, and finally a file with the same
194 basename is evaluated. Rename support in the updater-binary is
195 required for the latter checks to be used."""
196
197 result = tgtfiles.get("path:" + src.name)
198 if result is not None:
199 return result
200
201 if not OPTIONS.target_info_dict.get("update_rename_support", False):
202 return None
203
204 if src.size < 1000:
205 return None
206
207 result = tgtfiles.get("sha1:" + src.sha1)
208 if result is not None and existing.get(result.name) is None:
209 return result
210 result = tgtfiles.get("file:" + src.name.split("/")[-1])
211 if result is not None and existing.get(result.name) is None:
212 return result
213 return None
214
Dan Albert8b72aef2015-03-23 19:13:21 -0700215class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700216 def __init__(self, partition, fs_config):
217 self.partition = partition
218 self.fs_config = fs_config
219 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700220
Dan Albert8b72aef2015-03-23 19:13:21 -0700221 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700222 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700223 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700224 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700225
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700226 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700227 # The target_files contains a record of what the uid,
228 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700229 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700230
231 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700232 if not line:
233 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700234 columns = line.split()
235 name, uid, gid, mode = columns[:4]
236 selabel = None
237 capabilities = None
238
239 # After the first 4 columns, there are a series of key=value
240 # pairs. Extract out the fields we care about.
241 for element in columns[4:]:
242 key, value = element.split("=")
243 if key == "selabel":
244 selabel = value
245 if key == "capabilities":
246 capabilities = value
247
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700248 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700249 if i is not None:
250 i.uid = int(uid)
251 i.gid = int(gid)
252 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700253 i.selabel = selabel
254 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700255 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700256 i.children.sort(key=lambda i: i.name)
257
Tao Baof2cffbd2015-07-22 12:33:18 -0700258 # Set metadata for the files generated by this script. For full recovery
259 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700260 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700261 if i:
262 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700263 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700264 if i:
265 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700266
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700267
Dan Albert8b72aef2015-03-23 19:13:21 -0700268class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700269 """Items represent the metadata (user, group, mode) of files and
270 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700271 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700272 self.itemset = itemset
273 self.name = name
274 self.uid = None
275 self.gid = None
276 self.mode = None
277 self.selabel = None
278 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700279 self.is_dir = is_dir
280 self.descendants = None
281 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700282
283 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700284 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700285 self.parent.children.append(self)
286 else:
287 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700288 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700289 self.children = []
290
291 def Dump(self, indent=0):
292 if self.uid is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800293 print("%s%s %d %d %o" % (
294 " " * indent, self.name, self.uid, self.gid, self.mode))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700295 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800296 print("%s%s %s %s %s" % (
297 " " * indent, self.name, self.uid, self.gid, self.mode))
Dan Albert8b72aef2015-03-23 19:13:21 -0700298 if self.is_dir:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800299 print("%s%s" % (" " * indent, self.descendants))
300 print("%s%s" % (" " * indent, self.best_subtree))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700301 for i in self.children:
302 i.Dump(indent=indent+1)
303
Doug Zongkereef39442009-04-02 12:14:19 -0700304 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700305 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700306 all children and determine the best strategy for using set_perm_recursive
307 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700308 values. Recursively calls itself for all descendants.
309
Dan Albert8b72aef2015-03-23 19:13:21 -0700310 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
311 counting up all descendants of this node. (dmode or fmode may be None.)
312 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
313 fmode, selabel, capabilities) tuple that will match the most descendants of
314 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700315 """
316
Dan Albert8b72aef2015-03-23 19:13:21 -0700317 assert self.is_dir
318 key = (self.uid, self.gid, self.mode, None, self.selabel,
319 self.capabilities)
320 self.descendants = {key: 1}
321 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700322 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700323 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700324 for k, v in i.CountChildMetadata().iteritems():
325 d[k] = d.get(k, 0) + v
326 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700327 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700328 d[k] = d.get(k, 0) + 1
329
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700330 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
331 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700332
333 # First, find the (uid, gid) pair that matches the most
334 # descendants.
335 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700336 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700337 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
338 ug = MostPopularKey(ug, (0, 0))
339
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700340 # Now find the dmode, fmode, selabel, and capabilities that match
341 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700342 best_dmode = (0, 0o755)
343 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700344 best_selabel = (0, None)
345 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700346 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700347 if k[:2] != ug:
348 continue
349 if k[2] is not None and count >= best_dmode[0]:
350 best_dmode = (count, k[2])
351 if k[3] is not None and count >= best_fmode[0]:
352 best_fmode = (count, k[3])
353 if k[4] is not None and count >= best_selabel[0]:
354 best_selabel = (count, k[4])
355 if k[5] is not None and count >= best_capabilities[0]:
356 best_capabilities = (count, k[5])
357 self.best_subtree = ug + (
358 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700359
360 return d
361
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700362 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700363 """Append set_perm/set_perm_recursive commands to 'script' to
364 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700365 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700366
367 self.CountChildMetadata()
368
369 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700370 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
371 # that the current item (and all its children) have already been set to.
372 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700373 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700374 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700375 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700376 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700377 current = item.best_subtree
378
379 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700380 item.mode != current[2] or item.selabel != current[4] or \
381 item.capabilities != current[5]:
382 script.SetPermissions("/"+item.name, item.uid, item.gid,
383 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700384
385 for i in item.children:
386 recurse(i, current)
387 else:
388 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700389 item.mode != current[3] or item.selabel != current[4] or \
390 item.capabilities != current[5]:
391 script.SetPermissions("/"+item.name, item.uid, item.gid,
392 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700393
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700394 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700395
396
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700397def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
398 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700399 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800400 list of symlinks. output_zip may be None, in which case the copy is
401 skipped (but the other side effects still happen). substitute is an
402 optional dict of {output filename: contents} to be output instead of
403 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700404 """
405
406 symlinks = []
407
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700408 partition = itemset.partition
409
Doug Zongkereef39442009-04-02 12:14:19 -0700410 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700411 prefix = partition.upper() + "/"
412 if info.filename.startswith(prefix):
413 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700414 if IsSymlink(info):
415 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700416 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700417 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700418 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700419 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700420 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700421 if substitute and fn in substitute and substitute[fn] is None:
422 continue
423 if output_zip is not None:
424 if substitute and fn in substitute:
425 data = substitute[fn]
426 else:
427 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700428 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700429 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700430 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700431 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700432 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700433
434 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800435 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700436
437
Doug Zongkereef39442009-04-02 12:14:19 -0700438def SignOutput(temp_zip_name, output_zip_name):
439 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
440 pw = key_passwords[OPTIONS.package_key]
441
Doug Zongker951495f2009-08-14 12:44:19 -0700442 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
443 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700444
445
Dan Albert8b72aef2015-03-23 19:13:21 -0700446def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700447 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700448 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700449 device = GetBuildProp("ro.product.device", info_dict)
450 script.AssertDevice(device)
451 else:
452 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700453 raise common.ExternalError(
454 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700455 for prop in oem_props.split():
456 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700457 raise common.ExternalError(
458 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700459 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700460
Doug Zongkereef39442009-04-02 12:14:19 -0700461
Tao Baod42e97e2016-11-30 12:11:57 -0800462def _WriteRecoveryImageToBoot(script, output_zip):
463 """Find and write recovery image to /boot in two-step OTA.
464
465 In two-step OTAs, we write recovery image to /boot as the first step so that
466 we can reboot to there and install a new recovery image to /recovery.
467 A special "recovery-two-step.img" will be preferred, which encodes the correct
468 path of "/boot". Otherwise the device may show "device is corrupt" message
469 when booting into /boot.
470
471 Fall back to using the regular recovery.img if the two-step recovery image
472 doesn't exist. Note that rebuilding the special image at this point may be
473 infeasible, because we don't have the desired boot signer and keys when
474 calling ota_from_target_files.py.
475 """
476
477 recovery_two_step_img_name = "recovery-two-step.img"
478 recovery_two_step_img_path = os.path.join(
479 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
480 if os.path.exists(recovery_two_step_img_path):
481 recovery_two_step_img = common.GetBootableImage(
482 recovery_two_step_img_name, recovery_two_step_img_name,
483 OPTIONS.input_tmp, "RECOVERY")
484 common.ZipWriteStr(
485 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800486 print("two-step package: using %s in stage 1/3" % (
487 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800488 script.WriteRawImage("/boot", recovery_two_step_img_name)
489 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800490 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800491 # The "recovery.img" entry has been written into package earlier.
492 script.WriteRawImage("/boot", "recovery.img")
493
494
Doug Zongkerc9253822014-02-04 12:17:58 -0800495def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700496 namelist = [name for name in target_files_zip.namelist()]
497 return ("SYSTEM/recovery-from-boot.p" in namelist or
498 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700499
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700500def HasVendorPartition(target_files_zip):
501 try:
502 target_files_zip.getinfo("VENDOR/")
503 return True
504 except KeyError:
505 return False
506
Michael Runge6e836112014-04-15 17:40:21 -0700507def GetOemProperty(name, oem_props, oem_dict, info_dict):
508 if oem_props is not None and name in oem_props:
509 return oem_dict[name]
510 return GetBuildProp(name, info_dict)
511
512
513def CalculateFingerprint(oem_props, oem_dict, info_dict):
514 if oem_props is None:
515 return GetBuildProp("ro.build.fingerprint", info_dict)
516 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700517 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
518 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
519 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
520 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700521
Doug Zongkerfc44a512014-08-26 13:10:25 -0700522
Doug Zongker3c84f562014-07-31 11:06:30 -0700523def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700524 # Return an image object (suitable for passing to BlockImageDiff)
525 # for the 'which' partition (most be "system" or "vendor"). If a
526 # prebuilt image and file map are found in tmpdir they are used,
527 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700528
529 assert which in ("system", "vendor")
530
531 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700532 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
533 if os.path.exists(path) and os.path.exists(mappath):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800534 print("using %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700535 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700536
537 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800538 print("building %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700539
540 # This is an 'old' target-files, which does not contain images
541 # already built. Build them.
542
Doug Zongkerfc44a512014-08-26 13:10:25 -0700543 mappath = tempfile.mkstemp()[1]
544 OPTIONS.tempfiles.append(mappath)
545
Doug Zongker3c84f562014-07-31 11:06:30 -0700546 import add_img_to_target_files
547 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700548 path = add_img_to_target_files.BuildSystem(
549 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700550 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700551 path = add_img_to_target_files.BuildVendor(
552 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700553
Tao Baoff777812015-05-12 11:42:31 -0700554 # Bug: http://b/20939131
555 # In ext4 filesystems, block 0 might be changed even being mounted
556 # R/O. We add it to clobbered_blocks so that it will be written to the
557 # target unconditionally. Note that they are still part of care_map.
558 clobbered_blocks = "0"
559
560 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700561
562
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700563def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700564 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700565 # be installed on top of. For now, we expect the API just won't
566 # change very often. Similarly for fstab, it might have changed
567 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700568 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700569
Tao Bao838c68f2016-03-15 19:16:18 +0000570 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700571 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -0700572 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700573 if oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700574 if OPTIONS.oem_source is None:
575 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800576 if not OPTIONS.oem_no_mount:
577 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700578 oem_dict = common.LoadDictionaryFromLines(
579 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700580
Tao Bao3e30d972016-03-15 13:20:19 -0700581 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700582 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700583 "post-build": target_fp,
Dan Albert8b72aef2015-03-23 19:13:21 -0700584 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
585 OPTIONS.info_dict),
586 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
587 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700588
Doug Zongker05d3dea2009-06-22 11:32:31 -0700589 device_specific = common.DeviceSpecificParams(
590 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700591 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700592 output_zip=output_zip,
593 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700594 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700595 metadata=metadata,
596 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700597
Doug Zongkerc9253822014-02-04 12:17:58 -0800598 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800599 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800600
Tao Baod8d14be2016-02-04 14:26:02 -0800601 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
602
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700603 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
604 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
605 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700606
Michael Runge6e836112014-04-15 17:40:21 -0700607 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700608 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800609
610 # Two-step package strategy (in chronological order, which is *not*
611 # the order in which the generated script has things):
612 #
613 # if stage is not "2/3" or "3/3":
614 # write recovery image to boot partition
615 # set stage to "2/3"
616 # reboot to boot partition and restart recovery
617 # else if stage is "2/3":
618 # write recovery image to recovery partition
619 # set stage to "3/3"
620 # reboot to recovery partition and restart recovery
621 # else:
622 # (stage must be "3/3")
623 # set stage to ""
624 # do normal full package installation:
625 # wipe and install system, boot image, etc.
626 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700627 # complete script normally
628 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800629
630 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
631 OPTIONS.input_tmp, "RECOVERY")
632 if OPTIONS.two_step:
633 if not OPTIONS.info_dict.get("multistage_support", None):
634 assert False, "two-step packages not supported by this build"
635 fs = OPTIONS.info_dict["fstab"]["/misc"]
636 assert fs.fs_type.upper() == "EMMC", \
637 "two-step packages only supported on devices with EMMC /misc partitions"
638 bcb_dev = {"bcb_dev": fs.device}
639 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
640 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700641if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800642""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800643
644 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
645 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800646 script.WriteRawImage("/recovery", "recovery.img")
647 script.AppendExtra("""
648set_stage("%(bcb_dev)s", "3/3");
649reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700650else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800651""" % bcb_dev)
652
Tao Baod42e97e2016-11-30 12:11:57 -0800653 # Stage 3/3: Make changes.
654 script.Comment("Stage 3/3")
655
Tao Bao6c55a8a2015-04-08 15:30:27 -0700656 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700657 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700658
Doug Zongkere5ff5902012-01-17 10:55:37 -0800659 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700660
Doug Zongker01ce19c2014-02-04 13:48:15 -0800661 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700662
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700663 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800664 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700665 if HasVendorPartition(input_zip):
666 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700667
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400668 # Place a copy of file_contexts.bin into the OTA package which will be used
669 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700670 if "selinux_fc" in OPTIONS.info_dict:
671 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500672
Michael Runge7cd99ba2014-10-22 17:21:48 -0700673 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
674
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700675 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700676 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800677
Doug Zongker26e66192014-02-20 13:22:07 -0800678 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700679 # Full OTA is done as an "incremental" against an empty source
680 # image. This has the effect of writing new data from the package
681 # to the entire partition, but lets us reuse the updater code that
682 # writes incrementals to do it.
683 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
684 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700685 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700686 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800687 else:
688 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700689 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800690 if not has_recovery_patch:
691 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800692 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700693
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700694 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800695 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700696
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700697 boot_img = common.GetBootableImage(
698 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800699
Doug Zongker91a99c22014-05-09 13:15:01 -0700700 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800701 def output_sink(fn, data):
702 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700703 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800704
705 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
706 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700707
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700708 system_items.GetMetadata(input_zip)
709 system_items.Get("system").SetPermissions(script)
710
711 if HasVendorPartition(input_zip):
712 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
713 script.ShowProgress(0.1, 0)
714
715 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700716 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
717 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700718 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700719 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700720 else:
721 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700722 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700723 script.UnpackPackageDir("vendor", "/vendor")
724
725 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
726 script.MakeSymlinks(symlinks)
727
728 vendor_items.GetMetadata(input_zip)
729 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700730
Doug Zongker37974732010-09-16 17:44:38 -0700731 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700732 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700733
Doug Zongker01ce19c2014-02-04 13:48:15 -0800734 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700735 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700736
Doug Zongker01ce19c2014-02-04 13:48:15 -0800737 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700738 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700739
Doug Zongker1c390a22009-05-14 19:06:36 -0700740 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700741 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700742
Doug Zongker14833602010-02-02 13:12:04 -0800743 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800744
Doug Zongker922206e2014-03-04 13:16:24 -0800745 if OPTIONS.wipe_user_data:
746 script.ShowProgress(0.1, 10)
747 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700748
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800749 if OPTIONS.two_step:
750 script.AppendExtra("""
751set_stage("%(bcb_dev)s", "");
752""" % bcb_dev)
753 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800754
755 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
756 script.Comment("Stage 1/3")
757 _WriteRecoveryImageToBoot(script, output_zip)
758
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800759 script.AppendExtra("""
760set_stage("%(bcb_dev)s", "2/3");
761reboot_now("%(bcb_dev)s", "");
762endif;
763endif;
764""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800765
Tao Bao5d182562016-02-23 11:38:39 -0800766 script.SetProgress(1)
767 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800768 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700769 WriteMetadata(metadata, output_zip)
770
Doug Zongkerfc44a512014-08-26 13:10:25 -0700771
Dan Albert8e0178d2015-01-27 15:53:15 -0800772def WritePolicyConfig(file_name, output_zip):
773 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500774
Doug Zongker2ea21062010-04-28 16:05:21 -0700775
776def WriteMetadata(metadata, output_zip):
777 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
778 "".join(["%s=%s\n" % kv
779 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700780
Doug Zongkerfc44a512014-08-26 13:10:25 -0700781
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700782def LoadPartitionFiles(z, partition):
783 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700784 ZipFile, and return a dict of {filename: File object}."""
785 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700786 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700787 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700788 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700789 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700790 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700791 data = z.read(info.filename)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +0900792 out[fn] = common.File(fn, data, info.compress_size)
Doug Zongker1807e702012-02-28 12:21:08 -0800793 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700794
795
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700796def GetBuildProp(prop, info_dict):
797 """Return the fingerprint of the build of a given target-files info_dict."""
798 try:
799 return info_dict.get("build.prop", {})[prop]
800 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700801 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700802
Doug Zongkerfc44a512014-08-26 13:10:25 -0700803
Michael Runge4038aa82013-12-13 18:06:28 -0800804def AddToKnownPaths(filename, known_paths):
805 if filename[-1] == "/":
806 return
807 dirs = filename.split("/")[:-1]
808 while len(dirs) > 0:
809 path = "/".join(dirs)
810 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700811 break
Michael Runge4038aa82013-12-13 18:06:28 -0800812 known_paths.add(path)
813 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700814
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700815
Geremy Condra36bd3652014-02-06 19:45:10 -0800816def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700817 # TODO(tbao): We should factor out the common parts between
818 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800819 source_version = OPTIONS.source_info_dict["recovery_api_version"]
820 target_version = OPTIONS.target_info_dict["recovery_api_version"]
821
822 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700823 print("WARNING: generating edify script for a source that "
824 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700825 script = edify_generator.EdifyGenerator(
826 source_version, OPTIONS.target_info_dict,
827 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800828
Tao Bao3806c232015-07-05 21:08:33 -0700829 recovery_mount_options = OPTIONS.source_info_dict.get(
830 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700831 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
832 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Tao Bao3806c232015-07-05 21:08:33 -0700833 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700834 if source_oem_props or target_oem_props:
Tao Bao3806c232015-07-05 21:08:33 -0700835 if OPTIONS.oem_source is None:
836 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800837 if not OPTIONS.oem_no_mount:
838 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700839 oem_dict = common.LoadDictionaryFromLines(
840 open(OPTIONS.oem_source).readlines())
841
Dan Albert8b72aef2015-03-23 19:13:21 -0700842 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700843 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
844 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800845 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700846 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800847
Tao Bao5d182562016-02-23 11:38:39 -0800848 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
849 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
850 is_downgrade = long(post_timestamp) < long(pre_timestamp)
851
852 if OPTIONS.downgrade:
853 metadata["ota-downgrade"] = "yes"
854 if not is_downgrade:
855 raise RuntimeError("--downgrade specified but no downgrade detected: "
856 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
857 else:
858 if is_downgrade:
859 # Non-fatal here to allow generating such a package which may require
860 # manual work to adjust the post-timestamp. A legit use case is that we
861 # cut a new build C (after having A and B), but want to enfore the
862 # update path of A -> C -> B. Specifying --downgrade may not help since
863 # that would enforce a data wipe for C -> B update.
864 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
865 "The package may not be deployed properly. "
866 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
867 metadata["post-timestamp"] = post_timestamp
868
Geremy Condra36bd3652014-02-06 19:45:10 -0800869 device_specific = common.DeviceSpecificParams(
870 source_zip=source_zip,
871 source_version=source_version,
872 target_zip=target_zip,
873 target_version=target_version,
874 output_zip=output_zip,
875 script=script,
876 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700877 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800878
Tao Bao3e30d972016-03-15 13:20:19 -0700879 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700880 OPTIONS.source_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -0700881 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700882 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800883 metadata["pre-build"] = source_fp
884 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700885 metadata["pre-build-incremental"] = GetBuildProp(
886 "ro.build.version.incremental", OPTIONS.source_info_dict)
887 metadata["post-build-incremental"] = GetBuildProp(
888 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800889
890 source_boot = common.GetBootableImage(
891 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
892 OPTIONS.source_info_dict)
893 target_boot = common.GetBootableImage(
894 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
895 updating_boot = (not OPTIONS.two_step and
896 (source_boot.data != target_boot.data))
897
Geremy Condra36bd3652014-02-06 19:45:10 -0800898 target_recovery = common.GetBootableImage(
899 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800900
Doug Zongkerfc44a512014-08-26 13:10:25 -0700901 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
902 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700903
904 blockimgdiff_version = 1
905 if OPTIONS.info_dict:
906 blockimgdiff_version = max(
907 int(i) for i in
908 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
909
Tao Baof8acad12016-07-07 09:09:58 -0700910 # Check the first block of the source system partition for remount R/W only
911 # if the filesystem is ext4.
912 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
913 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700914 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
915 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
916 # b) the blocks listed in block map may not contain all the bytes for a given
917 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700918 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
919 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
920 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700921 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800922 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700923 version=blockimgdiff_version,
924 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700925
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700926 if HasVendorPartition(target_zip):
927 if not HasVendorPartition(source_zip):
928 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700929 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
930 OPTIONS.source_info_dict)
931 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
932 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800933
934 # Check first block of vendor partition for remount R/W only if
935 # disk type is ext4
936 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800937 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700938 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700939 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800940 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700941 version=blockimgdiff_version,
942 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700943 else:
944 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800945
Michael Rungec6e3afd2014-05-05 11:55:47 -0700946 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800947 device_specific.IncrementalOTA_Assertions()
948
949 # Two-step incremental package strategy (in chronological order,
950 # which is *not* the order in which the generated script has
951 # things):
952 #
953 # if stage is not "2/3" or "3/3":
954 # do verification on current system
955 # write recovery image to boot partition
956 # set stage to "2/3"
957 # reboot to boot partition and restart recovery
958 # else if stage is "2/3":
959 # write recovery image to recovery partition
960 # set stage to "3/3"
961 # reboot to recovery partition and restart recovery
962 # else:
963 # (stage must be "3/3")
964 # perform update:
965 # patch system files, etc.
966 # force full install of new boot image
967 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700968 # complete script normally
969 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800970
971 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700972 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800973 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700974 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800975 assert fs.fs_type.upper() == "EMMC", \
976 "two-step packages only supported on devices with EMMC /misc partitions"
977 bcb_dev = {"bcb_dev": fs.device}
978 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
979 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700980if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800981""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800982
983 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
984 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700985 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800986 script.WriteRawImage("/recovery", "recovery.img")
987 script.AppendExtra("""
988set_stage("%(bcb_dev)s", "3/3");
989reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700990else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800991""" % bcb_dev)
992
Tao Baod42e97e2016-11-30 12:11:57 -0800993 # Stage 1/3: (a) Verify the current system.
994 script.Comment("Stage 1/3")
995
Tao Bao6c55a8a2015-04-08 15:30:27 -0700996 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -0800997 script.Print("Source: %s" % (source_fp,))
998 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700999
Geremy Condra36bd3652014-02-06 19:45:10 -08001000 script.Print("Verifying current system...")
1001
1002 device_specific.IncrementalOTA_VerifyBegin()
1003
Tao Bao3e30d972016-03-15 13:20:19 -07001004 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1005 # patching on a device that's already on the target build will damage the
1006 # system. Because operations like move don't check the block state, they
1007 # always apply the changes unconditionally.
1008 if blockimgdiff_version <= 2:
1009 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001010 script.AssertSomeFingerprint(source_fp)
1011 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001012 script.AssertSomeThumbprint(
1013 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001014
1015 else: # blockimgdiff_version > 2
1016 if source_oem_props is None and target_oem_props is None:
1017 script.AssertSomeFingerprint(source_fp, target_fp)
1018 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001019 script.AssertSomeThumbprint(
1020 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1021 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001022 elif source_oem_props is None and target_oem_props is not None:
1023 script.AssertFingerprintOrThumbprint(
1024 source_fp,
1025 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1026 else:
1027 script.AssertFingerprintOrThumbprint(
1028 target_fp,
1029 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001030
Tao Baod8d14be2016-02-04 14:26:02 -08001031 # Check the required cache size (i.e. stashed blocks).
1032 size = []
1033 if system_diff:
1034 size.append(system_diff.required_cache)
1035 if vendor_diff:
1036 size.append(vendor_diff.required_cache)
1037
Geremy Condra36bd3652014-02-06 19:45:10 -08001038 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001039 boot_type, boot_device = common.GetTypeAndDevice(
1040 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001041 d = common.Difference(target_boot, source_boot)
1042 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001043 if d is None:
1044 include_full_boot = True
1045 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1046 else:
1047 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001048
Tao Bao89fbb0f2017-01-10 10:47:58 -08001049 print("boot target: %d source: %d diff: %d" % (
1050 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001051
Doug Zongkerf8340082014-08-05 10:39:37 -07001052 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001053
Doug Zongkerf8340082014-08-05 10:39:37 -07001054 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1055 (boot_type, boot_device,
1056 source_boot.size, source_boot.sha1,
1057 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001058 size.append(target_boot.size)
1059
1060 if size:
1061 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001062
1063 device_specific.IncrementalOTA_VerifyEnd()
1064
1065 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001066 # Stage 1/3: (b) Write recovery image to /boot.
1067 _WriteRecoveryImageToBoot(script, output_zip)
1068
Geremy Condra36bd3652014-02-06 19:45:10 -08001069 script.AppendExtra("""
1070set_stage("%(bcb_dev)s", "2/3");
1071reboot_now("%(bcb_dev)s", "");
1072else
1073""" % bcb_dev)
1074
Tao Baod42e97e2016-11-30 12:11:57 -08001075 # Stage 3/3: Make changes.
1076 script.Comment("Stage 3/3")
1077
Jesse Zhao75bcea02015-01-06 10:59:53 -08001078 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001079 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001080 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001081 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001082
Geremy Condra36bd3652014-02-06 19:45:10 -08001083 script.Comment("---- start making changes here ----")
1084
1085 device_specific.IncrementalOTA_InstallBegin()
1086
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001087 system_diff.WriteScript(script, output_zip,
1088 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001089
Doug Zongkerfc44a512014-08-26 13:10:25 -07001090 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001091 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001092
1093 if OPTIONS.two_step:
1094 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1095 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001096 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001097
1098 if not OPTIONS.two_step:
1099 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001100 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001101 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001102 script.Print("Installing boot image...")
1103 script.WriteRawImage("/boot", "boot.img")
1104 else:
1105 # Produce the boot image by applying a patch to the current
1106 # contents of the boot partition, and write it back to the
1107 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001108 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001109 script.Print("Patching boot image...")
1110 script.ShowProgress(0.1, 10)
1111 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1112 % (boot_type, boot_device,
1113 source_boot.size, source_boot.sha1,
1114 target_boot.size, target_boot.sha1),
1115 "-",
1116 target_boot.size, target_boot.sha1,
1117 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001118 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001119 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001120
1121 # Do device-specific installation (eg, write radio image).
1122 device_specific.IncrementalOTA_InstallEnd()
1123
1124 if OPTIONS.extra_script is not None:
1125 script.AppendExtra(OPTIONS.extra_script)
1126
Doug Zongker922206e2014-03-04 13:16:24 -08001127 if OPTIONS.wipe_user_data:
1128 script.Print("Erasing user data...")
1129 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001130 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001131
Geremy Condra36bd3652014-02-06 19:45:10 -08001132 if OPTIONS.two_step:
1133 script.AppendExtra("""
1134set_stage("%(bcb_dev)s", "");
1135endif;
1136endif;
1137""" % bcb_dev)
1138
1139 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001140 # For downgrade OTAs, we prefer to use the update-binary in the source
1141 # build that is actually newer than the one in the target build.
1142 if OPTIONS.downgrade:
1143 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1144 else:
1145 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001146 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001147 WriteMetadata(metadata, output_zip)
1148
Doug Zongker32b527d2014-03-04 10:03:02 -08001149
Tao Bao9bc6bb22015-11-09 16:58:28 -08001150def WriteVerifyPackage(input_zip, output_zip):
1151 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1152
1153 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1154 recovery_mount_options = OPTIONS.info_dict.get(
1155 "recovery_mount_options")
1156 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001157 if oem_props:
Tao Bao9bc6bb22015-11-09 16:58:28 -08001158 if OPTIONS.oem_source is None:
1159 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -08001160 if not OPTIONS.oem_no_mount:
1161 script.Mount("/oem", recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001162 oem_dict = common.LoadDictionaryFromLines(
1163 open(OPTIONS.oem_source).readlines())
1164
1165 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1166 metadata = {
1167 "post-build": target_fp,
1168 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1169 OPTIONS.info_dict),
1170 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1171 }
1172
1173 device_specific = common.DeviceSpecificParams(
1174 input_zip=input_zip,
1175 input_version=OPTIONS.info_dict["recovery_api_version"],
1176 output_zip=output_zip,
1177 script=script,
1178 input_tmp=OPTIONS.input_tmp,
1179 metadata=metadata,
1180 info_dict=OPTIONS.info_dict)
1181
1182 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1183
1184 script.Print("Verifying device images against %s..." % target_fp)
1185 script.AppendExtra("")
1186
1187 script.Print("Verifying boot...")
1188 boot_img = common.GetBootableImage(
1189 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1190 boot_type, boot_device = common.GetTypeAndDevice(
1191 "/boot", OPTIONS.info_dict)
1192 script.Verify("%s:%s:%d:%s" % (
1193 boot_type, boot_device, boot_img.size, boot_img.sha1))
1194 script.AppendExtra("")
1195
1196 script.Print("Verifying recovery...")
1197 recovery_img = common.GetBootableImage(
1198 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1199 recovery_type, recovery_device = common.GetTypeAndDevice(
1200 "/recovery", OPTIONS.info_dict)
1201 script.Verify("%s:%s:%d:%s" % (
1202 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1203 script.AppendExtra("")
1204
1205 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1206 system_tgt.ResetFileMap()
1207 system_diff = common.BlockDifference("system", system_tgt, src=None)
1208 system_diff.WriteStrictVerifyScript(script)
1209
1210 if HasVendorPartition(input_zip):
1211 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1212 vendor_tgt.ResetFileMap()
1213 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1214 vendor_diff.WriteStrictVerifyScript(script)
1215
1216 # Device specific partitions, such as radio, bootloader and etc.
1217 device_specific.VerifyOTA_Assertions()
1218
1219 script.SetProgress(1.0)
1220 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001221 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001222 WriteMetadata(metadata, output_zip)
1223
1224
Tao Baoc098e9e2016-01-07 13:03:56 -08001225def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1226 source_file=None):
1227 """Generate an Android OTA package that has A/B update payload."""
1228
Tao Baoc96316c2017-01-24 22:10:49 -08001229 def ComputeStreamingMetadata(zip_file):
1230 """Compute the streaming metadata for a given zip."""
1231
1232 def ComputeEntryOffsetSize(name):
1233 """Compute the zip entry offset and size."""
1234 info = zip_file.getinfo(name)
1235 offset = info.header_offset + len(info.FileHeader())
1236 size = info.file_size
1237 return '%s:%d:%d' % (name, offset, size)
1238
1239 # payload.bin and payload_properties.txt must exist.
1240 offsets = [ComputeEntryOffsetSize('payload.bin'),
1241 ComputeEntryOffsetSize('payload_properties.txt')]
1242
1243 # care_map.txt is available only if dm-verity is enabled.
1244 if 'care_map.txt' in zip_file.namelist():
1245 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
1246 return ','.join(offsets)
1247
Alex Deymod8d96ec2016-06-10 16:38:31 -07001248 # The place where the output from the subprocess should go.
1249 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1250
Tao Baoc098e9e2016-01-07 13:03:56 -08001251 # Setup signing keys.
1252 if OPTIONS.package_key is None:
1253 OPTIONS.package_key = OPTIONS.info_dict.get(
1254 "default_system_dev_certificate",
1255 "build/target/product/security/testkey")
1256
Tao Baodea0f8b2016-06-20 17:55:06 -07001257 # A/B updater expects a signing key in RSA format. Gets the key ready for
1258 # later use in step 3, unless a payload_signer has been specified.
1259 if OPTIONS.payload_signer is None:
1260 cmd = ["openssl", "pkcs8",
1261 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1262 "-inform", "DER", "-nocrypt"]
1263 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1264 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001265 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1266 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001267 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001268
Tao Baodea0f8b2016-06-20 17:55:06 -07001269 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001270 temp_zip_file = tempfile.NamedTemporaryFile()
1271 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1272 compression=zipfile.ZIP_DEFLATED)
1273
1274 # Metadata to comply with Android OTA package format.
1275 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1276 oem_dict = None
1277 if oem_props:
1278 if OPTIONS.oem_source is None:
1279 raise common.ExternalError("OEM source required for this build")
1280 oem_dict = common.LoadDictionaryFromLines(
1281 open(OPTIONS.oem_source).readlines())
1282
1283 metadata = {
1284 "post-build": CalculateFingerprint(oem_props, oem_dict,
1285 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001286 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1287 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001288 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1289 OPTIONS.info_dict),
1290 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001291 "ota-required-cache": "0",
1292 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001293 }
1294
1295 if source_file is not None:
1296 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1297 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001298 metadata["pre-build-incremental"] = GetBuildProp(
1299 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001300
1301 # 1. Generate payload.
1302 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1303 cmd = ["brillo_update_payload", "generate",
1304 "--payload", payload_file,
1305 "--target_image", target_file]
1306 if source_file is not None:
1307 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001308 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1309 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001310 assert p1.returncode == 0, "brillo_update_payload generate failed"
1311
1312 # 2. Generate hashes of the payload and metadata files.
1313 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1314 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1315 cmd = ["brillo_update_payload", "hash",
1316 "--unsigned_payload", payload_file,
1317 "--signature_size", "256",
1318 "--metadata_hash_file", metadata_sig_file,
1319 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001320 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1321 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001322 assert p1.returncode == 0, "brillo_update_payload hash failed"
1323
1324 # 3. Sign the hashes and insert them back into the payload file.
1325 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1326 suffix=".bin")
1327 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1328 suffix=".bin")
1329 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001330 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001331 cmd = [OPTIONS.payload_signer]
1332 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001333 else:
1334 cmd = ["openssl", "pkeyutl", "-sign",
1335 "-inkey", rsa_key,
1336 "-pkeyopt", "digest:sha256"]
1337 cmd.extend(["-in", payload_sig_file,
1338 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001339 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1340 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001341 assert p1.returncode == 0, "openssl sign payload failed"
1342
1343 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001344 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001345 cmd = [OPTIONS.payload_signer]
1346 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001347 else:
1348 cmd = ["openssl", "pkeyutl", "-sign",
1349 "-inkey", rsa_key,
1350 "-pkeyopt", "digest:sha256"]
1351 cmd.extend(["-in", metadata_sig_file,
1352 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001353 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1354 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001355 assert p1.returncode == 0, "openssl sign metadata failed"
1356
1357 # 3c. Insert the signatures back into the payload file.
1358 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1359 suffix=".bin")
1360 cmd = ["brillo_update_payload", "sign",
1361 "--unsigned_payload", payload_file,
1362 "--payload", signed_payload_file,
1363 "--signature_size", "256",
1364 "--metadata_signature_file", signed_metadata_sig_file,
1365 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001366 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1367 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001368 assert p1.returncode == 0, "brillo_update_payload sign failed"
1369
Alex Deymo19241c12016-02-04 22:29:29 -08001370 # 4. Dump the signed payload properties.
1371 properties_file = common.MakeTempFile(prefix="payload-properties-",
1372 suffix=".txt")
1373 cmd = ["brillo_update_payload", "properties",
1374 "--payload", signed_payload_file,
1375 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001376 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1377 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001378 assert p1.returncode == 0, "brillo_update_payload properties failed"
1379
Tao Bao7c5dc572016-06-14 17:48:11 -07001380 if OPTIONS.wipe_user_data:
1381 with open(properties_file, "a") as f:
1382 f.write("POWERWASH=1\n")
1383 metadata["ota-wipe"] = "yes"
1384
Tao Baoc96316c2017-01-24 22:10:49 -08001385 # Add the signed payload file and properties into the zip. In order to
1386 # support streaming, we pack payload.bin, payload_properties.txt and
1387 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1388 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001389 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1390 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001391 common.ZipWrite(output_zip, properties_file,
1392 arcname="payload_properties.txt",
1393 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001394
Tianjie Xucfa86222016-03-07 16:31:19 -08001395 # If dm-verity is supported for the device, copy contents of care_map
1396 # into A/B OTA package.
1397 if OPTIONS.info_dict.get("verity") == "true":
1398 target_zip = zipfile.ZipFile(target_file, "r")
1399 care_map_path = "META/care_map.txt"
1400 namelist = target_zip.namelist()
1401 if care_map_path in namelist:
1402 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001403 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1404 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001405 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001406 print("Warning: cannot find care map file in target_file package")
Tianjie Xucfa86222016-03-07 16:31:19 -08001407 common.ZipClose(target_zip)
1408
Tao Baoc96316c2017-01-24 22:10:49 -08001409 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1410 # zip entries, as well as padding the entry headers. We sign the current
1411 # package (without the metadata entry) to allow that to happen. Then compute
1412 # the zip entry offsets, write the metadata and do the signing again.
Tao Baoc098e9e2016-01-07 13:03:56 -08001413 common.ZipClose(output_zip)
Tao Baoc96316c2017-01-24 22:10:49 -08001414 temp_signing = tempfile.NamedTemporaryFile()
1415 SignOutput(temp_zip_file.name, temp_signing.name)
Tao Baoc098e9e2016-01-07 13:03:56 -08001416 temp_zip_file.close()
1417
Tao Baoc96316c2017-01-24 22:10:49 -08001418 # Open the signed zip. Compute the metadata that's needed for streaming.
1419 output_zip = zipfile.ZipFile(temp_signing, "a",
1420 compression=zipfile.ZIP_DEFLATED)
Tao Baobfdcb122017-01-31 15:06:05 -08001421 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1422 output_zip)
Tao Baoc96316c2017-01-24 22:10:49 -08001423
1424 # Write the metadata entry into the zip.
1425 WriteMetadata(metadata, output_zip)
1426 common.ZipClose(output_zip)
1427
1428 # Re-sign the package after adding the metadata entry, which should not
1429 # affect the entries that are needed for streaming. Because signapk packs
1430 # ZIP_STORED entries first, then the ZIP_DEFLATED entries such as metadata.
1431 SignOutput(temp_signing.name, output_file)
1432 temp_signing.close()
1433
1434 # Reopen the signed zip to double check the streaming metadata.
1435 output_zip = zipfile.ZipFile(output_file, "r")
Tao Baobfdcb122017-01-31 15:06:05 -08001436 assert (metadata['ota-streaming-property-files'] ==
Tao Baoc96316c2017-01-24 22:10:49 -08001437 ComputeStreamingMetadata(output_zip)), \
1438 "Mismatching streaming metadata."
1439 common.ZipClose(output_zip)
1440
Tao Baoc098e9e2016-01-07 13:03:56 -08001441
Dan Albert8b72aef2015-03-23 19:13:21 -07001442class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001443 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001444 self.deferred_patch_list = None
Tao Bao89fbb0f2017-01-10 10:47:58 -08001445 print("Loading target...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001446 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001447 print("Loading source...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001448 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1449
1450 self.verbatim_targets = verbatim_targets = []
1451 self.patch_list = patch_list = []
1452 diffs = []
1453 self.renames = renames = {}
1454 known_paths = set()
1455 largest_source_size = 0
1456
1457 matching_file_cache = {}
1458 for fn, sf in source_data.items():
1459 assert fn == sf.name
1460 matching_file_cache["path:" + fn] = sf
1461 if fn in target_data.keys():
1462 AddToKnownPaths(fn, known_paths)
1463 # Only allow eligibility for filename/sha matching
1464 # if there isn't a perfect path match.
1465 if target_data.get(sf.name) is None:
1466 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1467 matching_file_cache["sha:" + sf.sha1] = sf
1468
1469 for fn in sorted(target_data.keys()):
1470 tf = target_data[fn]
1471 assert fn == tf.name
1472 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1473 if sf is not None and sf.name != tf.name:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001474 print("File has moved from " + sf.name + " to " + tf.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001475 renames[sf.name] = tf
1476
1477 if sf is None or fn in OPTIONS.require_verbatim:
1478 # This file should be included verbatim
1479 if fn in OPTIONS.prohibit_verbatim:
1480 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Tao Bao89fbb0f2017-01-10 10:47:58 -08001481 print("send", fn, "verbatim")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001482 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001483 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001484 if fn in target_data.keys():
1485 AddToKnownPaths(fn, known_paths)
1486 elif tf.sha1 != sf.sha1:
1487 # File is different; consider sending as a patch
1488 diffs.append(common.Difference(tf, sf))
1489 else:
1490 # Target file data identical to source (may still be renamed)
1491 pass
1492
1493 common.ComputeDifferences(diffs)
1494
1495 for diff in diffs:
1496 tf, sf, d = diff.GetPatch()
1497 path = "/".join(tf.name.split("/")[:-1])
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001498 if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001499 path not in known_paths:
1500 # patch is almost as big as the file; don't bother patching
1501 # or a patch + rename cannot take place due to the target
1502 # directory not existing
1503 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001504 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001505 if sf.name in renames:
1506 del renames[sf.name]
1507 AddToKnownPaths(tf.name, known_paths)
1508 else:
1509 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1510 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1511 largest_source_size = max(largest_source_size, sf.size)
1512
1513 self.largest_source_size = largest_source_size
1514
1515 def EmitVerification(self, script):
1516 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001517 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001518 if tf.name != sf.name:
1519 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1520 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1521 so_far += sf.size
1522 return so_far
1523
Michael Runge63f01de2014-10-28 19:24:19 -07001524 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001525 for fn, _, sha1 in self.verbatim_targets:
1526 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001527 script.FileCheck("/"+fn, sha1)
1528 for tf, _, _, _ in self.patch_list:
1529 script.FileCheck(tf.name, tf.sha1)
1530
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001531 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001532 file_list = ["/" + i[0] for i in self.verbatim_targets]
1533 file_list += ["/" + i for i in self.source_data
1534 if i not in self.target_data and i not in self.renames]
1535 file_list += list(extras)
1536 # Sort the list in descending order, which removes all the files first
1537 # before attempting to remove the folder. (Bug: 22960996)
1538 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001539
1540 def TotalPatchSize(self):
1541 return sum(i[1].size for i in self.patch_list)
1542
1543 def EmitPatches(self, script, total_patch_size, so_far):
1544 self.deferred_patch_list = deferred_patch_list = []
1545 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001546 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001547 if tf.name == "system/build.prop":
1548 deferred_patch_list.append(item)
1549 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001550 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001551 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001552 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1553 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001554 so_far += tf.size
1555 script.SetProgress(so_far / total_patch_size)
1556 return so_far
1557
1558 def EmitDeferredPatches(self, script):
1559 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001560 tf, sf, _, _ = item
1561 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1562 "patch/" + sf.name + ".p")
1563 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001564
1565 def EmitRenames(self, script):
1566 if len(self.renames) > 0:
1567 script.Print("Renaming files...")
1568 for src, tgt in self.renames.iteritems():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001569 print("Renaming " + src + " to " + tgt.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001570 script.RenameFile(src, tgt.name)
1571
1572
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001573def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001574 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1575 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1576
Doug Zongker26e66192014-02-20 13:22:07 -08001577 if (OPTIONS.block_based and
1578 target_has_recovery_patch and
1579 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001580 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1581
Doug Zongker37974732010-09-16 17:44:38 -07001582 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1583 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001584
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001585 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001586 print("WARNING: generating edify script for a source that "
1587 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001588 script = edify_generator.EdifyGenerator(
1589 source_version, OPTIONS.target_info_dict,
1590 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001591
Tao Bao34b47bf2015-06-22 19:17:41 -07001592 recovery_mount_options = OPTIONS.source_info_dict.get(
1593 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001594 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1595 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -07001596 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001597 if source_oem_props or target_oem_props:
Michael Runge6e836112014-04-15 17:40:21 -07001598 if OPTIONS.oem_source is None:
1599 raise common.ExternalError("OEM source required for this build")
Tao Bao1bb5a182016-03-04 09:45:03 -08001600 if not OPTIONS.oem_no_mount:
1601 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001602 oem_dict = common.LoadDictionaryFromLines(
1603 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001604
Dan Albert8b72aef2015-03-23 19:13:21 -07001605 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001606 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
1607 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001608 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001609 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001610
Tao Bao5d182562016-02-23 11:38:39 -08001611 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
1612 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1613 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1614
1615 if OPTIONS.downgrade:
1616 metadata["ota-downgrade"] = "yes"
1617 if not is_downgrade:
1618 raise RuntimeError("--downgrade specified but no downgrade detected: "
1619 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
1620 else:
1621 if is_downgrade:
1622 # Non-fatal here to allow generating such a package which may require
1623 # manual work to adjust the post-timestamp. A legit use case is that we
1624 # cut a new build C (after having A and B), but want to enfore the
1625 # update path of A -> C -> B. Specifying --downgrade may not help since
1626 # that would enforce a data wipe for C -> B update.
1627 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
1628 "The package may not be deployed properly. "
1629 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
1630 metadata["post-timestamp"] = post_timestamp
1631
Doug Zongker05d3dea2009-06-22 11:32:31 -07001632 device_specific = common.DeviceSpecificParams(
1633 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001634 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001635 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001636 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001637 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001638 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001639 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001640 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001641
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001642 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001643 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001644 if HasVendorPartition(target_zip):
1645 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001646 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001647 else:
1648 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001649
Tao Bao3e30d972016-03-15 13:20:19 -07001650 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001651 OPTIONS.target_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -07001652 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001653 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001654
Tao Bao3e30d972016-03-15 13:20:19 -07001655 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001656 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001657 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001658 script.AssertSomeThumbprint(
1659 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1660 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001661 elif source_oem_props is None and target_oem_props is not None:
1662 script.AssertFingerprintOrThumbprint(
1663 source_fp,
1664 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1665 else:
1666 script.AssertFingerprintOrThumbprint(
1667 target_fp,
1668 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001669
Doug Zongker2ea21062010-04-28 16:05:21 -07001670 metadata["pre-build"] = source_fp
1671 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001672 metadata["pre-build-incremental"] = GetBuildProp(
1673 "ro.build.version.incremental", OPTIONS.source_info_dict)
1674 metadata["post-build-incremental"] = GetBuildProp(
1675 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001676
Doug Zongker55d93282011-01-25 17:03:34 -08001677 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001678 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1679 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001680 target_boot = common.GetBootableImage(
1681 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001682 updating_boot = (not OPTIONS.two_step and
1683 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001684
Doug Zongker55d93282011-01-25 17:03:34 -08001685 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001686 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1687 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001688 target_recovery = common.GetBootableImage(
1689 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001690 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001691
Doug Zongker881dd402009-09-20 14:03:55 -07001692 # Here's how we divide up the progress bar:
1693 # 0.1 for verifying the start state (PatchCheck calls)
1694 # 0.8 for applying patches (ApplyPatch calls)
1695 # 0.1 for unpacking verbatim files, symlinking, and doing the
1696 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001697
Michael Runge6e836112014-04-15 17:40:21 -07001698 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001699 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001700
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001701 # Two-step incremental package strategy (in chronological order,
1702 # which is *not* the order in which the generated script has
1703 # things):
1704 #
1705 # if stage is not "2/3" or "3/3":
1706 # do verification on current system
1707 # write recovery image to boot partition
1708 # set stage to "2/3"
1709 # reboot to boot partition and restart recovery
1710 # else if stage is "2/3":
1711 # write recovery image to recovery partition
1712 # set stage to "3/3"
1713 # reboot to recovery partition and restart recovery
1714 # else:
1715 # (stage must be "3/3")
1716 # perform update:
1717 # patch system files, etc.
1718 # force full install of new boot image
1719 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001720 # complete script normally
1721 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001722
1723 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001724 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001725 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001726 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001727 assert fs.fs_type.upper() == "EMMC", \
1728 "two-step packages only supported on devices with EMMC /misc partitions"
1729 bcb_dev = {"bcb_dev": fs.device}
1730 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1731 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001732if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001733""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001734
1735 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1736 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001737 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001738 script.WriteRawImage("/recovery", "recovery.img")
1739 script.AppendExtra("""
1740set_stage("%(bcb_dev)s", "3/3");
1741reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001742else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001743""" % bcb_dev)
1744
Tao Baod42e97e2016-11-30 12:11:57 -08001745 # Stage 1/3: (a) Verify the current system.
1746 script.Comment("Stage 1/3")
1747
Tao Bao6c55a8a2015-04-08 15:30:27 -07001748 # Dump fingerprints
1749 script.Print("Source: %s" % (source_fp,))
1750 script.Print("Target: %s" % (target_fp,))
1751
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001752 script.Print("Verifying current system...")
1753
Doug Zongkere5ff5902012-01-17 10:55:37 -08001754 device_specific.IncrementalOTA_VerifyBegin()
1755
Doug Zongker881dd402009-09-20 14:03:55 -07001756 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001757 so_far = system_diff.EmitVerification(script)
1758 if vendor_diff:
1759 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001760
Tao Baod8d14be2016-02-04 14:26:02 -08001761 size = []
1762 if system_diff.patch_list:
1763 size.append(system_diff.largest_source_size)
1764 if vendor_diff:
1765 if vendor_diff.patch_list:
1766 size.append(vendor_diff.largest_source_size)
1767
Doug Zongker5da317e2009-06-02 13:38:17 -07001768 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001769 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001770 _, _, d = d.ComputePatch()
Tao Bao89fbb0f2017-01-10 10:47:58 -08001771 print("boot target: %d source: %d diff: %d" % (
1772 target_boot.size, source_boot.size, len(d)))
Doug Zongker5da317e2009-06-02 13:38:17 -07001773
Doug Zongker048e7ca2009-06-15 14:31:53 -07001774 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001775
Tao Baodd24da92015-07-29 14:09:23 -07001776 boot_type, boot_device = common.GetTypeAndDevice(
1777 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001778
1779 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1780 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001781 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001782 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001783 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001784 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001785
Tao Baod8d14be2016-02-04 14:26:02 -08001786 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001787 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001788
Doug Zongker05d3dea2009-06-22 11:32:31 -07001789 device_specific.IncrementalOTA_VerifyEnd()
1790
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001791 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001792 # Stage 1/3: (b) Write recovery image to /boot.
1793 _WriteRecoveryImageToBoot(script, output_zip)
1794
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001795 script.AppendExtra("""
1796set_stage("%(bcb_dev)s", "2/3");
1797reboot_now("%(bcb_dev)s", "");
1798else
1799""" % bcb_dev)
1800
Tao Baod42e97e2016-11-30 12:11:57 -08001801 # Stage 3/3: Make changes.
1802 script.Comment("Stage 3/3")
1803
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001804 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001805
Doug Zongkere5ff5902012-01-17 10:55:37 -08001806 device_specific.IncrementalOTA_InstallBegin()
1807
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001808 if OPTIONS.two_step:
1809 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1810 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001811 print("writing full boot image (forced by two-step mode)")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001812
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001813 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001814 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1815 if vendor_diff:
1816 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001817
Doug Zongker881dd402009-09-20 14:03:55 -07001818 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001819 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1820 if vendor_diff:
1821 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001822 if updating_boot:
1823 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001824
1825 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001826 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1827 if vendor_diff:
1828 script.Print("Patching vendor files...")
1829 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001830
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001831 if not OPTIONS.two_step:
1832 if updating_boot:
1833 # Produce the boot image by applying a patch to the current
1834 # contents of the boot partition, and write it back to the
1835 # partition.
1836 script.Print("Patching boot image...")
1837 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1838 % (boot_type, boot_device,
1839 source_boot.size, source_boot.sha1,
1840 target_boot.size, target_boot.sha1),
1841 "-",
1842 target_boot.size, target_boot.sha1,
1843 source_boot.sha1, "patch/boot.img.p")
1844 so_far += target_boot.size
1845 script.SetProgress(so_far / total_patch_size)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001846 print("boot image changed; including.")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001847 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001848 print("boot image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001849
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001850 system_items = ItemSet("system", "META/filesystem_config.txt")
1851 if vendor_diff:
1852 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1853
Doug Zongkereef39442009-04-02 12:14:19 -07001854 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001855 # Recovery is generated as a patch using both the boot image
1856 # (which contains the same linux kernel as recovery) and the file
1857 # /system/etc/recovery-resource.dat (which contains all the images
1858 # used in the recovery UI) as sources. This lets us minimize the
1859 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001860 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001861 # For older builds where recovery-resource.dat is not present, we
1862 # use only the boot image as the source.
1863
Doug Zongkerc9253822014-02-04 12:17:58 -08001864 if not target_has_recovery_patch:
1865 def output_sink(fn, data):
1866 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001867 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001868
1869 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1870 target_recovery, target_boot)
1871 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001872 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001873 "/system/etc/install-recovery.sh"])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001874 print("recovery image changed; including as patch from boot.")
Doug Zongkereef39442009-04-02 12:14:19 -07001875 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001876 print("recovery image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001877
Doug Zongker881dd402009-09-20 14:03:55 -07001878 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001879
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001880 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1881 if vendor_diff:
1882 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1883
1884 temp_script = script.MakeTemporary()
1885 system_items.GetMetadata(target_zip)
1886 system_items.Get("system").SetPermissions(temp_script)
1887 if vendor_diff:
1888 vendor_items.GetMetadata(target_zip)
1889 vendor_items.Get("vendor").SetPermissions(temp_script)
1890
1891 # Note that this call will mess up the trees of Items, so make sure
1892 # we're done with them.
1893 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1894 if vendor_diff:
1895 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001896
1897 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001898 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1899
1900 # Delete all the symlinks in source that aren't in target. This
1901 # needs to happen before verbatim files are unpacked, in case a
1902 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001903
1904 # If a symlink in the source will be replaced by a regular file, we cannot
1905 # delete the symlink/file in case the package gets applied again. For such
1906 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1907 # (Bug: 23646151)
1908 replaced_symlinks = dict()
1909 if system_diff:
1910 for i in system_diff.verbatim_targets:
1911 replaced_symlinks["/%s" % (i[0],)] = i[2]
1912 if vendor_diff:
1913 for i in vendor_diff.verbatim_targets:
1914 replaced_symlinks["/%s" % (i[0],)] = i[2]
1915
1916 if system_diff:
1917 for tf in system_diff.renames.values():
1918 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1919 if vendor_diff:
1920 for tf in vendor_diff.renames.values():
1921 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1922
1923 always_delete = []
1924 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001925 for dest, link in source_symlinks:
1926 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001927 if link in replaced_symlinks:
1928 may_delete.append((link, replaced_symlinks[link]))
1929 else:
1930 always_delete.append(link)
1931 script.DeleteFiles(always_delete)
1932 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001933
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001934 if system_diff.verbatim_targets:
1935 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001936 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001937 if vendor_diff and vendor_diff.verbatim_targets:
1938 script.Print("Unpacking new vendor files...")
1939 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001940
Doug Zongkerc9253822014-02-04 12:17:58 -08001941 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001942 script.Print("Unpacking new recovery...")
1943 script.UnpackPackageDir("recovery", "/system")
1944
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001945 system_diff.EmitRenames(script)
1946 if vendor_diff:
1947 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001948
Doug Zongker05d3dea2009-06-22 11:32:31 -07001949 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001950
1951 # Create all the symlinks that don't already exist, or point to
1952 # somewhere different than what we want. Delete each symlink before
1953 # creating it, since the 'symlink' command won't overwrite.
1954 to_create = []
1955 for dest, link in target_symlinks:
1956 if link in source_symlinks_d:
1957 if dest != source_symlinks_d[link]:
1958 to_create.append((dest, link))
1959 else:
1960 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001961 script.DeleteFiles([i[1] for i in to_create])
1962 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001963
1964 # Now that the symlinks are created, we can set all the
1965 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001966 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001967
Doug Zongker881dd402009-09-20 14:03:55 -07001968 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001969 device_specific.IncrementalOTA_InstallEnd()
1970
Doug Zongker1c390a22009-05-14 19:06:36 -07001971 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001972 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001973
Doug Zongkere92f15a2011-08-26 13:46:40 -07001974 # Patch the build.prop file last, so if something fails but the
1975 # device can still come up, it appears to be the old build and will
1976 # get set the OTA package again to retry.
1977 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001978 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001979
Doug Zongker922206e2014-03-04 13:16:24 -08001980 if OPTIONS.wipe_user_data:
1981 script.Print("Erasing user data...")
1982 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001983 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001984
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001985 if OPTIONS.two_step:
1986 script.AppendExtra("""
1987set_stage("%(bcb_dev)s", "");
1988endif;
1989endif;
1990""" % bcb_dev)
1991
Michael Runge63f01de2014-10-28 19:24:19 -07001992 if OPTIONS.verify and system_diff:
1993 script.Print("Remounting and verifying system partition files...")
1994 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001995 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001996 system_diff.EmitExplicitTargetVerification(script)
1997
1998 if OPTIONS.verify and vendor_diff:
1999 script.Print("Remounting and verifying vendor partition files...")
2000 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08002001 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002002 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08002003
2004 # For downgrade OTAs, we prefer to use the update-binary in the source
2005 # build that is actually newer than the one in the target build.
2006 if OPTIONS.downgrade:
2007 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
2008 else:
2009 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07002010
Tao Baod8d14be2016-02-04 14:26:02 -08002011 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07002012 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07002013
2014
2015def main(argv):
2016
2017 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08002018 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002019 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07002020 elif o in ("-k", "--package_key"):
2021 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002022 elif o in ("-i", "--incremental_from"):
2023 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002024 elif o == "--full_radio":
2025 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002026 elif o == "--full_bootloader":
2027 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002028 elif o in ("-w", "--wipe_user_data"):
2029 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002030 elif o == "--downgrade":
2031 OPTIONS.downgrade = True
2032 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07002033 elif o in ("-o", "--oem_settings"):
2034 OPTIONS.oem_source = a
Tao Bao8608cde2016-02-25 19:49:55 -08002035 elif o == "--oem_no_mount":
2036 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002037 elif o in ("-e", "--extra_script"):
2038 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002039 elif o in ("-t", "--worker_threads"):
2040 if a.isdigit():
2041 OPTIONS.worker_threads = int(a)
2042 else:
2043 raise ValueError("Cannot parse value %r for option %r - only "
2044 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002045 elif o in ("-2", "--two_step"):
2046 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002047 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002048 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002049 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002050 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002051 elif o == "--block":
2052 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002053 elif o in ("-b", "--binary"):
2054 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002055 elif o in ("--no_fallback_to_full",):
2056 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002057 elif o == "--stash_threshold":
2058 try:
2059 OPTIONS.stash_threshold = float(a)
2060 except ValueError:
2061 raise ValueError("Cannot parse value %r for option %r - expecting "
2062 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002063 elif o == "--gen_verify":
2064 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002065 elif o == "--log_diff":
2066 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002067 elif o == "--payload_signer":
2068 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002069 elif o == "--payload_signer_args":
2070 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002071 else:
2072 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002073 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002074
2075 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08002076 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002077 extra_long_opts=[
2078 "board_config=",
2079 "package_key=",
2080 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002081 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002082 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002083 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002084 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07002085 "extra_script=",
2086 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002087 "two_step",
2088 "no_signing",
2089 "block",
2090 "binary=",
2091 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002092 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002093 "verify",
2094 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002095 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002096 "gen_verify",
2097 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002098 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002099 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002100 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002101
2102 if len(args) != 2:
2103 common.Usage(__doc__)
2104 sys.exit(1)
2105
Tao Bao5d182562016-02-23 11:38:39 -08002106 if OPTIONS.downgrade:
2107 # Sanity check to enforce a data wipe.
2108 if not OPTIONS.wipe_user_data:
2109 raise ValueError("Cannot downgrade without a data wipe")
2110
2111 # We should only allow downgrading incrementals (as opposed to full).
2112 # Otherwise the device may go back from arbitrary build with this full
2113 # OTA package.
2114 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002115 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002116
Tao Baoc098e9e2016-01-07 13:03:56 -08002117 # Load the dict file from the zip directly to have a peek at the OTA type.
2118 # For packages using A/B update, unzipping is not needed.
2119 input_zip = zipfile.ZipFile(args[0], "r")
2120 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2121 common.ZipClose(input_zip)
2122
2123 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2124
2125 if ab_update:
2126 if OPTIONS.incremental_source is not None:
2127 OPTIONS.target_info_dict = OPTIONS.info_dict
2128 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2129 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2130 common.ZipClose(source_zip)
2131
2132 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002133 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002134 common.DumpInfoDict(OPTIONS.info_dict)
2135
2136 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002137 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002138 common.DumpInfoDict(OPTIONS.source_info_dict)
2139
2140 WriteABOTAPackageWithBrilloScript(
2141 target_file=args[0],
2142 output_file=args[1],
2143 source_file=OPTIONS.incremental_source)
2144
Tao Bao89fbb0f2017-01-10 10:47:58 -08002145 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002146 return
2147
Doug Zongker1c390a22009-05-14 19:06:36 -07002148 if OPTIONS.extra_script is not None:
2149 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2150
Tao Bao89fbb0f2017-01-10 10:47:58 -08002151 print("unzipping target target-files...")
Doug Zongker55d93282011-01-25 17:03:34 -08002152 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002153
Doug Zongkereef39442009-04-02 12:14:19 -07002154 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002155 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002156
Doug Zongker37974732010-09-16 17:44:38 -07002157 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002158 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07002159 common.DumpInfoDict(OPTIONS.info_dict)
2160
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002161 # If the caller explicitly specified the device-specific extensions
2162 # path via -s/--device_specific, use that. Otherwise, use
2163 # META/releasetools.py if it is present in the target target_files.
2164 # Otherwise, take the path of the file from 'tool_extensions' in the
2165 # info dict and look for that in the local filesystem, relative to
2166 # the current directory.
2167
Doug Zongker37974732010-09-16 17:44:38 -07002168 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002169 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2170 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08002171 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002172 OPTIONS.device_specific = from_input
2173 else:
2174 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2175
Doug Zongker37974732010-09-16 17:44:38 -07002176 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002177 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002178
Tao Baoc098e9e2016-01-07 13:03:56 -08002179 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002180 raise common.ExternalError(
2181 "--- target build has specified no recovery ---")
2182
Tao Bao767e3ac2015-11-10 12:19:19 -08002183 # Use the default key to sign the package if not specified with package_key.
2184 if not OPTIONS.no_signing:
2185 if OPTIONS.package_key is None:
2186 OPTIONS.package_key = OPTIONS.info_dict.get(
2187 "default_system_dev_certificate",
2188 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002189
Tao Bao767e3ac2015-11-10 12:19:19 -08002190 # Set up the output zip. Create a temporary zip file if signing is needed.
2191 if OPTIONS.no_signing:
2192 if os.path.exists(args[1]):
2193 os.unlink(args[1])
2194 output_zip = zipfile.ZipFile(args[1], "w",
2195 compression=zipfile.ZIP_DEFLATED)
2196 else:
2197 temp_zip_file = tempfile.NamedTemporaryFile()
2198 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2199 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002200
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002201 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002202 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002203 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002204 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002205 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002206
Tao Bao9bc6bb22015-11-09 16:58:28 -08002207 # Generate a verify package.
2208 if OPTIONS.gen_verify:
2209 WriteVerifyPackage(input_zip, output_zip)
2210
Tao Bao767e3ac2015-11-10 12:19:19 -08002211 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002212 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002213 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002214
2215 # Generate an incremental OTA. It will fall back to generate a full OTA on
2216 # failure unless no_fallback_to_full is specified.
2217 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002218 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08002219 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2220 OPTIONS.incremental_source)
2221 OPTIONS.target_info_dict = OPTIONS.info_dict
2222 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2223 OPTIONS.source_tmp)
2224 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002225 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002226 common.DumpInfoDict(OPTIONS.source_info_dict)
2227 try:
2228 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002229 if OPTIONS.log_diff:
2230 out_file = open(OPTIONS.log_diff, 'w')
2231 import target_files_diff
2232 target_files_diff.recursiveDiff('',
2233 OPTIONS.source_tmp,
2234 OPTIONS.input_tmp,
2235 out_file)
2236 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002237 except ValueError:
2238 if not OPTIONS.fallback_to_full:
2239 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08002240 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002241 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002242 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002243
Tao Bao767e3ac2015-11-10 12:19:19 -08002244 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002245
Tao Bao767e3ac2015-11-10 12:19:19 -08002246 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002247 if not OPTIONS.no_signing:
2248 SignOutput(temp_zip_file.name, args[1])
2249 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002250
Tao Bao89fbb0f2017-01-10 10:47:58 -08002251 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002252
2253
2254if __name__ == '__main__':
2255 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002256 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002257 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002258 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002259 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002260 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002261 finally:
2262 common.Cleanup()