blob: 72e00b25e67cf8644acd8227487f284fa3f887bd [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
Tao Bao2dd1c482017-02-03 16:49:39 -0800129import copy
Doug Zongkerfc44a512014-08-26 13:10:25 -0700130import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800131import os.path
Tao Baoc098e9e2016-01-07 13:03:56 -0800132import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700133import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700134import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700135import zipfile
136
137import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700138import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700139import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700140
141OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700142OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700143OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700144OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700145OPTIONS.require_verbatim = set()
146OPTIONS.prohibit_verbatim = set(("system/build.prop",))
147OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700148OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800149OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700150OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700151OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
152if OPTIONS.worker_threads == 0:
153 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800154OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900155OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800156OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800157OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700158OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800159OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700160OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700161OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700162OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700163# Stash size cannot exceed cache_size * threshold.
164OPTIONS.cache_size = None
165OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800166OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800167OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700168OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700169OPTIONS.payload_signer_args = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700170
Tao Bao2dd1c482017-02-03 16:49:39 -0800171METADATA_NAME = 'META-INF/com/android/metadata'
172
Doug Zongkereef39442009-04-02 12:14:19 -0700173def MostPopularKey(d, default):
174 """Given a dict, return the key corresponding to the largest
175 value. Returns 'default' if the dict is empty."""
176 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700177 if not x:
178 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700179 x.sort()
180 return x[-1][1]
181
182
183def IsSymlink(info):
184 """Return true if the zipfile.ZipInfo object passed in represents a
185 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700186 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700187
Hristo Bojinov96be7202010-08-02 10:26:17 -0700188def IsRegular(info):
189 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700190 regular file."""
191 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700192
Michael Runge4038aa82013-12-13 18:06:28 -0800193def ClosestFileMatch(src, tgtfiles, existing):
194 """Returns the closest file match between a source file and list
195 of potential matches. The exact filename match is preferred,
196 then the sha1 is searched for, and finally a file with the same
197 basename is evaluated. Rename support in the updater-binary is
198 required for the latter checks to be used."""
199
200 result = tgtfiles.get("path:" + src.name)
201 if result is not None:
202 return result
203
204 if not OPTIONS.target_info_dict.get("update_rename_support", False):
205 return None
206
207 if src.size < 1000:
208 return None
209
210 result = tgtfiles.get("sha1:" + src.sha1)
211 if result is not None and existing.get(result.name) is None:
212 return result
213 result = tgtfiles.get("file:" + src.name.split("/")[-1])
214 if result is not None and existing.get(result.name) is None:
215 return result
216 return None
217
Dan Albert8b72aef2015-03-23 19:13:21 -0700218class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700219 def __init__(self, partition, fs_config):
220 self.partition = partition
221 self.fs_config = fs_config
222 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700223
Dan Albert8b72aef2015-03-23 19:13:21 -0700224 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700225 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700226 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700227 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700228
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700229 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700230 # The target_files contains a record of what the uid,
231 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700232 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700233
234 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700235 if not line:
236 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700237 columns = line.split()
238 name, uid, gid, mode = columns[:4]
239 selabel = None
240 capabilities = None
241
242 # After the first 4 columns, there are a series of key=value
243 # pairs. Extract out the fields we care about.
244 for element in columns[4:]:
245 key, value = element.split("=")
246 if key == "selabel":
247 selabel = value
248 if key == "capabilities":
249 capabilities = value
250
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700251 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700252 if i is not None:
253 i.uid = int(uid)
254 i.gid = int(gid)
255 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700256 i.selabel = selabel
257 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700258 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700259 i.children.sort(key=lambda i: i.name)
260
Tao Baof2cffbd2015-07-22 12:33:18 -0700261 # Set metadata for the files generated by this script. For full recovery
262 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700263 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700264 if i:
265 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700266 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700267 if i:
268 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700269
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700270
Dan Albert8b72aef2015-03-23 19:13:21 -0700271class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700272 """Items represent the metadata (user, group, mode) of files and
273 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700274 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700275 self.itemset = itemset
276 self.name = name
277 self.uid = None
278 self.gid = None
279 self.mode = None
280 self.selabel = None
281 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700282 self.is_dir = is_dir
283 self.descendants = None
284 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700285
286 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700287 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700288 self.parent.children.append(self)
289 else:
290 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700291 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700292 self.children = []
293
294 def Dump(self, indent=0):
295 if self.uid is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800296 print("%s%s %d %d %o" % (
297 " " * indent, self.name, self.uid, self.gid, self.mode))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700298 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800299 print("%s%s %s %s %s" % (
300 " " * indent, self.name, self.uid, self.gid, self.mode))
Dan Albert8b72aef2015-03-23 19:13:21 -0700301 if self.is_dir:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800302 print("%s%s" % (" " * indent, self.descendants))
303 print("%s%s" % (" " * indent, self.best_subtree))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700304 for i in self.children:
305 i.Dump(indent=indent+1)
306
Doug Zongkereef39442009-04-02 12:14:19 -0700307 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700308 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700309 all children and determine the best strategy for using set_perm_recursive
310 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700311 values. Recursively calls itself for all descendants.
312
Dan Albert8b72aef2015-03-23 19:13:21 -0700313 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
314 counting up all descendants of this node. (dmode or fmode may be None.)
315 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
316 fmode, selabel, capabilities) tuple that will match the most descendants of
317 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700318 """
319
Dan Albert8b72aef2015-03-23 19:13:21 -0700320 assert self.is_dir
321 key = (self.uid, self.gid, self.mode, None, self.selabel,
322 self.capabilities)
323 self.descendants = {key: 1}
324 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700325 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700326 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700327 for k, v in i.CountChildMetadata().iteritems():
328 d[k] = d.get(k, 0) + v
329 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700330 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700331 d[k] = d.get(k, 0) + 1
332
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700333 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
334 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700335
336 # First, find the (uid, gid) pair that matches the most
337 # descendants.
338 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700339 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700340 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
341 ug = MostPopularKey(ug, (0, 0))
342
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700343 # Now find the dmode, fmode, selabel, and capabilities that match
344 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700345 best_dmode = (0, 0o755)
346 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700347 best_selabel = (0, None)
348 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700349 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700350 if k[:2] != ug:
351 continue
352 if k[2] is not None and count >= best_dmode[0]:
353 best_dmode = (count, k[2])
354 if k[3] is not None and count >= best_fmode[0]:
355 best_fmode = (count, k[3])
356 if k[4] is not None and count >= best_selabel[0]:
357 best_selabel = (count, k[4])
358 if k[5] is not None and count >= best_capabilities[0]:
359 best_capabilities = (count, k[5])
360 self.best_subtree = ug + (
361 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700362
363 return d
364
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700365 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700366 """Append set_perm/set_perm_recursive commands to 'script' to
367 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700368 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700369
370 self.CountChildMetadata()
371
372 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700373 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
374 # that the current item (and all its children) have already been set to.
375 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700376 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700377 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700378 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700379 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700380 current = item.best_subtree
381
382 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700383 item.mode != current[2] or item.selabel != current[4] or \
384 item.capabilities != current[5]:
385 script.SetPermissions("/"+item.name, item.uid, item.gid,
386 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700387
388 for i in item.children:
389 recurse(i, current)
390 else:
391 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700392 item.mode != current[3] or item.selabel != current[4] or \
393 item.capabilities != current[5]:
394 script.SetPermissions("/"+item.name, item.uid, item.gid,
395 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700396
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700397 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700398
399
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700400def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
401 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700402 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800403 list of symlinks. output_zip may be None, in which case the copy is
404 skipped (but the other side effects still happen). substitute is an
405 optional dict of {output filename: contents} to be output instead of
406 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700407 """
408
409 symlinks = []
410
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700411 partition = itemset.partition
412
Doug Zongkereef39442009-04-02 12:14:19 -0700413 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700414 prefix = partition.upper() + "/"
415 if info.filename.startswith(prefix):
416 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700417 if IsSymlink(info):
418 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700419 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700420 else:
421 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700422 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700423 if substitute and fn in substitute and substitute[fn] is None:
424 continue
425 if output_zip is not None:
426 if substitute and fn in substitute:
427 data = substitute[fn]
428 else:
429 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700430 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700431 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700432 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700433 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700434 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700435
436 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800437 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700438
439
Doug Zongkereef39442009-04-02 12:14:19 -0700440def SignOutput(temp_zip_name, output_zip_name):
441 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
442 pw = key_passwords[OPTIONS.package_key]
443
Doug Zongker951495f2009-08-14 12:44:19 -0700444 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
445 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700446
447
Dan Albert8b72aef2015-03-23 19:13:21 -0700448def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700449 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700450 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700451 device = GetBuildProp("ro.product.device", info_dict)
452 script.AssertDevice(device)
453 else:
454 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700455 raise common.ExternalError(
456 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700457 for prop in oem_props.split():
458 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700459 raise common.ExternalError(
460 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700461 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700462
Doug Zongkereef39442009-04-02 12:14:19 -0700463
Tao Baod42e97e2016-11-30 12:11:57 -0800464def _WriteRecoveryImageToBoot(script, output_zip):
465 """Find and write recovery image to /boot in two-step OTA.
466
467 In two-step OTAs, we write recovery image to /boot as the first step so that
468 we can reboot to there and install a new recovery image to /recovery.
469 A special "recovery-two-step.img" will be preferred, which encodes the correct
470 path of "/boot". Otherwise the device may show "device is corrupt" message
471 when booting into /boot.
472
473 Fall back to using the regular recovery.img if the two-step recovery image
474 doesn't exist. Note that rebuilding the special image at this point may be
475 infeasible, because we don't have the desired boot signer and keys when
476 calling ota_from_target_files.py.
477 """
478
479 recovery_two_step_img_name = "recovery-two-step.img"
480 recovery_two_step_img_path = os.path.join(
481 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
482 if os.path.exists(recovery_two_step_img_path):
483 recovery_two_step_img = common.GetBootableImage(
484 recovery_two_step_img_name, recovery_two_step_img_name,
485 OPTIONS.input_tmp, "RECOVERY")
486 common.ZipWriteStr(
487 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800488 print("two-step package: using %s in stage 1/3" % (
489 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800490 script.WriteRawImage("/boot", recovery_two_step_img_name)
491 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800492 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800493 # The "recovery.img" entry has been written into package earlier.
494 script.WriteRawImage("/boot", "recovery.img")
495
496
Doug Zongkerc9253822014-02-04 12:17:58 -0800497def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700498 namelist = [name for name in target_files_zip.namelist()]
499 return ("SYSTEM/recovery-from-boot.p" in namelist or
500 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700501
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700502def HasVendorPartition(target_files_zip):
503 try:
504 target_files_zip.getinfo("VENDOR/")
505 return True
506 except KeyError:
507 return False
508
Michael Runge6e836112014-04-15 17:40:21 -0700509def GetOemProperty(name, oem_props, oem_dict, info_dict):
510 if oem_props is not None and name in oem_props:
511 return oem_dict[name]
512 return GetBuildProp(name, info_dict)
513
514
515def CalculateFingerprint(oem_props, oem_dict, info_dict):
516 if oem_props is None:
517 return GetBuildProp("ro.build.fingerprint", info_dict)
518 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700519 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
520 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
521 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
522 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700523
Doug Zongkerfc44a512014-08-26 13:10:25 -0700524
Doug Zongker3c84f562014-07-31 11:06:30 -0700525def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700526 # Return an image object (suitable for passing to BlockImageDiff)
527 # for the 'which' partition (most be "system" or "vendor"). If a
528 # prebuilt image and file map are found in tmpdir they are used,
529 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700530
531 assert which in ("system", "vendor")
532
533 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700534 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
535 if os.path.exists(path) and os.path.exists(mappath):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800536 print("using %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700537 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700538
539 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800540 print("building %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700541
542 # This is an 'old' target-files, which does not contain images
543 # already built. Build them.
544
Doug Zongkerfc44a512014-08-26 13:10:25 -0700545 mappath = tempfile.mkstemp()[1]
546 OPTIONS.tempfiles.append(mappath)
547
Doug Zongker3c84f562014-07-31 11:06:30 -0700548 import add_img_to_target_files
549 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700550 path = add_img_to_target_files.BuildSystem(
551 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700552 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700553 path = add_img_to_target_files.BuildVendor(
554 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700555
Tao Baoff777812015-05-12 11:42:31 -0700556 # Bug: http://b/20939131
557 # In ext4 filesystems, block 0 might be changed even being mounted
558 # R/O. We add it to clobbered_blocks so that it will be written to the
559 # target unconditionally. Note that they are still part of care_map.
560 clobbered_blocks = "0"
561
562 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700563
564
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700565def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700566 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700567 # be installed on top of. For now, we expect the API just won't
568 # change very often. Similarly for fstab, it might have changed
569 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700570 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700571
Tao Bao838c68f2016-03-15 19:16:18 +0000572 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700573 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -0700574 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700575 if oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700576 if OPTIONS.oem_source is None:
577 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800578 if not OPTIONS.oem_no_mount:
579 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700580 oem_dict = common.LoadDictionaryFromLines(
581 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700582
Tao Bao3e30d972016-03-15 13:20:19 -0700583 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700584 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700585 "post-build": target_fp,
Dan Albert8b72aef2015-03-23 19:13:21 -0700586 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
587 OPTIONS.info_dict),
588 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
589 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700590
Doug Zongker05d3dea2009-06-22 11:32:31 -0700591 device_specific = common.DeviceSpecificParams(
592 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700593 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700594 output_zip=output_zip,
595 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700596 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700597 metadata=metadata,
598 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700599
Doug Zongkerc9253822014-02-04 12:17:58 -0800600 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800601 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800602
Tao Baod8d14be2016-02-04 14:26:02 -0800603 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
604
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700605 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
606 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
607 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700608
Michael Runge6e836112014-04-15 17:40:21 -0700609 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700610 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800611
612 # Two-step package strategy (in chronological order, which is *not*
613 # the order in which the generated script has things):
614 #
615 # if stage is not "2/3" or "3/3":
616 # write recovery image to boot partition
617 # set stage to "2/3"
618 # reboot to boot partition and restart recovery
619 # else if stage is "2/3":
620 # write recovery image to recovery partition
621 # set stage to "3/3"
622 # reboot to recovery partition and restart recovery
623 # else:
624 # (stage must be "3/3")
625 # set stage to ""
626 # do normal full package installation:
627 # wipe and install system, boot image, etc.
628 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700629 # complete script normally
630 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800631
632 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
633 OPTIONS.input_tmp, "RECOVERY")
634 if OPTIONS.two_step:
635 if not OPTIONS.info_dict.get("multistage_support", None):
636 assert False, "two-step packages not supported by this build"
637 fs = OPTIONS.info_dict["fstab"]["/misc"]
638 assert fs.fs_type.upper() == "EMMC", \
639 "two-step packages only supported on devices with EMMC /misc partitions"
640 bcb_dev = {"bcb_dev": fs.device}
641 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
642 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700643if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800644""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800645
646 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
647 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800648 script.WriteRawImage("/recovery", "recovery.img")
649 script.AppendExtra("""
650set_stage("%(bcb_dev)s", "3/3");
651reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700652else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800653""" % bcb_dev)
654
Tao Baod42e97e2016-11-30 12:11:57 -0800655 # Stage 3/3: Make changes.
656 script.Comment("Stage 3/3")
657
Tao Bao6c55a8a2015-04-08 15:30:27 -0700658 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700659 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700660
Doug Zongkere5ff5902012-01-17 10:55:37 -0800661 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700662
Doug Zongker01ce19c2014-02-04 13:48:15 -0800663 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700664
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700665 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800666 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700667 if HasVendorPartition(input_zip):
668 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700669
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400670 # Place a copy of file_contexts.bin into the OTA package which will be used
671 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700672 if "selinux_fc" in OPTIONS.info_dict:
673 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500674
Michael Runge7cd99ba2014-10-22 17:21:48 -0700675 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
676
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700677 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700678 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800679
Doug Zongker26e66192014-02-20 13:22:07 -0800680 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700681 # Full OTA is done as an "incremental" against an empty source
682 # image. This has the effect of writing new data from the package
683 # to the entire partition, but lets us reuse the updater code that
684 # writes incrementals to do it.
685 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
686 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700687 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700688 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800689 else:
690 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700691 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800692 if not has_recovery_patch:
693 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800694 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700695
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700696 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800697 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700698
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700699 boot_img = common.GetBootableImage(
700 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800701
Doug Zongker91a99c22014-05-09 13:15:01 -0700702 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800703 def output_sink(fn, data):
704 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700705 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800706
707 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
708 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700709
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700710 system_items.GetMetadata(input_zip)
711 system_items.Get("system").SetPermissions(script)
712
713 if HasVendorPartition(input_zip):
714 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
715 script.ShowProgress(0.1, 0)
716
717 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700718 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
719 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700720 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700721 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700722 else:
723 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700724 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700725 script.UnpackPackageDir("vendor", "/vendor")
726
727 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
728 script.MakeSymlinks(symlinks)
729
730 vendor_items.GetMetadata(input_zip)
731 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700732
Doug Zongker37974732010-09-16 17:44:38 -0700733 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700734 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700735
Doug Zongker01ce19c2014-02-04 13:48:15 -0800736 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700737 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700738
Doug Zongker01ce19c2014-02-04 13:48:15 -0800739 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700740 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700741
Doug Zongker1c390a22009-05-14 19:06:36 -0700742 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700743 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700744
Doug Zongker14833602010-02-02 13:12:04 -0800745 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800746
Doug Zongker922206e2014-03-04 13:16:24 -0800747 if OPTIONS.wipe_user_data:
748 script.ShowProgress(0.1, 10)
749 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700750
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800751 if OPTIONS.two_step:
752 script.AppendExtra("""
753set_stage("%(bcb_dev)s", "");
754""" % bcb_dev)
755 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800756
757 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
758 script.Comment("Stage 1/3")
759 _WriteRecoveryImageToBoot(script, output_zip)
760
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800761 script.AppendExtra("""
762set_stage("%(bcb_dev)s", "2/3");
763reboot_now("%(bcb_dev)s", "");
764endif;
765endif;
766""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800767
Tao Bao5d182562016-02-23 11:38:39 -0800768 script.SetProgress(1)
769 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800770 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700771 WriteMetadata(metadata, output_zip)
772
Doug Zongkerfc44a512014-08-26 13:10:25 -0700773
Dan Albert8e0178d2015-01-27 15:53:15 -0800774def WritePolicyConfig(file_name, output_zip):
775 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500776
Doug Zongker2ea21062010-04-28 16:05:21 -0700777
778def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800779 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
780 common.ZipWriteStr(output_zip, METADATA_NAME, value,
781 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700782
Doug Zongkerfc44a512014-08-26 13:10:25 -0700783
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700784def LoadPartitionFiles(z, partition):
785 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700786 ZipFile, and return a dict of {filename: File object}."""
787 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700788 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700789 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700790 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700791 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700792 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700793 data = z.read(info.filename)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +0900794 out[fn] = common.File(fn, data, info.compress_size)
Doug Zongker1807e702012-02-28 12:21:08 -0800795 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700796
797
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700798def GetBuildProp(prop, info_dict):
799 """Return the fingerprint of the build of a given target-files info_dict."""
800 try:
801 return info_dict.get("build.prop", {})[prop]
802 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700803 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700804
Doug Zongkerfc44a512014-08-26 13:10:25 -0700805
Michael Runge4038aa82013-12-13 18:06:28 -0800806def AddToKnownPaths(filename, known_paths):
807 if filename[-1] == "/":
808 return
809 dirs = filename.split("/")[:-1]
810 while len(dirs) > 0:
811 path = "/".join(dirs)
812 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700813 break
Michael Runge4038aa82013-12-13 18:06:28 -0800814 known_paths.add(path)
815 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700816
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700817
Tao Baob31892e2017-02-07 11:21:17 -0800818def HandleDowngradeMetadata(metadata):
819 # Only incremental OTAs are allowed to reach here.
820 assert OPTIONS.incremental_source is not None
821
822 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
823 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
824 is_downgrade = long(post_timestamp) < long(pre_timestamp)
825
826 if OPTIONS.downgrade:
827 metadata["ota-downgrade"] = "yes"
828 if not is_downgrade:
829 raise RuntimeError("--downgrade specified but no downgrade detected: "
830 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
831 else:
832 if is_downgrade:
833 # Non-fatal here to allow generating such a package which may require
834 # manual work to adjust the post-timestamp. A legit use case is that we
835 # cut a new build C (after having A and B), but want to enfore the
836 # update path of A -> C -> B. Specifying --downgrade may not help since
837 # that would enforce a data wipe for C -> B update.
838 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
839 "The package may not be deployed properly. "
840 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
841 metadata["post-timestamp"] = post_timestamp
842
843
Geremy Condra36bd3652014-02-06 19:45:10 -0800844def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700845 # TODO(tbao): We should factor out the common parts between
846 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800847 source_version = OPTIONS.source_info_dict["recovery_api_version"]
848 target_version = OPTIONS.target_info_dict["recovery_api_version"]
849
850 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700851 print("WARNING: generating edify script for a source that "
852 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700853 script = edify_generator.EdifyGenerator(
854 source_version, OPTIONS.target_info_dict,
855 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800856
Tao Bao3806c232015-07-05 21:08:33 -0700857 recovery_mount_options = OPTIONS.source_info_dict.get(
858 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700859 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
860 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Tao Bao3806c232015-07-05 21:08:33 -0700861 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700862 if source_oem_props or target_oem_props:
Tao Bao3806c232015-07-05 21:08:33 -0700863 if OPTIONS.oem_source is None:
864 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800865 if not OPTIONS.oem_no_mount:
866 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700867 oem_dict = common.LoadDictionaryFromLines(
868 open(OPTIONS.oem_source).readlines())
869
Dan Albert8b72aef2015-03-23 19:13:21 -0700870 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700871 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
872 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800873 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700874 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800875
Tao Baob31892e2017-02-07 11:21:17 -0800876 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800877
Geremy Condra36bd3652014-02-06 19:45:10 -0800878 device_specific = common.DeviceSpecificParams(
879 source_zip=source_zip,
880 source_version=source_version,
881 target_zip=target_zip,
882 target_version=target_version,
883 output_zip=output_zip,
884 script=script,
885 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700886 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800887
Tao Bao3e30d972016-03-15 13:20:19 -0700888 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700889 OPTIONS.source_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -0700890 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700891 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800892 metadata["pre-build"] = source_fp
893 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700894 metadata["pre-build-incremental"] = GetBuildProp(
895 "ro.build.version.incremental", OPTIONS.source_info_dict)
896 metadata["post-build-incremental"] = GetBuildProp(
897 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800898
899 source_boot = common.GetBootableImage(
900 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
901 OPTIONS.source_info_dict)
902 target_boot = common.GetBootableImage(
903 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
904 updating_boot = (not OPTIONS.two_step and
905 (source_boot.data != target_boot.data))
906
Geremy Condra36bd3652014-02-06 19:45:10 -0800907 target_recovery = common.GetBootableImage(
908 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800909
Doug Zongkerfc44a512014-08-26 13:10:25 -0700910 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
911 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700912
913 blockimgdiff_version = 1
914 if OPTIONS.info_dict:
915 blockimgdiff_version = max(
916 int(i) for i in
917 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
918
Tao Baof8acad12016-07-07 09:09:58 -0700919 # Check the first block of the source system partition for remount R/W only
920 # if the filesystem is ext4.
921 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
922 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700923 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
924 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
925 # b) the blocks listed in block map may not contain all the bytes for a given
926 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700927 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
928 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
929 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700930 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800931 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700932 version=blockimgdiff_version,
933 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700934
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700935 if HasVendorPartition(target_zip):
936 if not HasVendorPartition(source_zip):
937 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700938 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
939 OPTIONS.source_info_dict)
940 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
941 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800942
943 # Check first block of vendor partition for remount R/W only if
944 # disk type is ext4
945 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800946 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700947 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700948 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800949 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700950 version=blockimgdiff_version,
951 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700952 else:
953 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800954
Michael Rungec6e3afd2014-05-05 11:55:47 -0700955 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800956 device_specific.IncrementalOTA_Assertions()
957
958 # Two-step incremental package strategy (in chronological order,
959 # which is *not* the order in which the generated script has
960 # things):
961 #
962 # if stage is not "2/3" or "3/3":
963 # do verification on current system
964 # write recovery image to boot partition
965 # set stage to "2/3"
966 # reboot to boot partition and restart recovery
967 # else if stage is "2/3":
968 # write recovery image to recovery partition
969 # set stage to "3/3"
970 # reboot to recovery partition and restart recovery
971 # else:
972 # (stage must be "3/3")
973 # perform update:
974 # patch system files, etc.
975 # force full install of new boot image
976 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700977 # complete script normally
978 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800979
980 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700981 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800982 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700983 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800984 assert fs.fs_type.upper() == "EMMC", \
985 "two-step packages only supported on devices with EMMC /misc partitions"
986 bcb_dev = {"bcb_dev": fs.device}
987 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
988 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700989if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800990""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800991
992 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
993 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700994 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800995 script.WriteRawImage("/recovery", "recovery.img")
996 script.AppendExtra("""
997set_stage("%(bcb_dev)s", "3/3");
998reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700999else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001000""" % bcb_dev)
1001
Tao Baod42e97e2016-11-30 12:11:57 -08001002 # Stage 1/3: (a) Verify the current system.
1003 script.Comment("Stage 1/3")
1004
Tao Bao6c55a8a2015-04-08 15:30:27 -07001005 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -08001006 script.Print("Source: %s" % (source_fp,))
1007 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001008
Geremy Condra36bd3652014-02-06 19:45:10 -08001009 script.Print("Verifying current system...")
1010
1011 device_specific.IncrementalOTA_VerifyBegin()
1012
Tao Bao3e30d972016-03-15 13:20:19 -07001013 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1014 # patching on a device that's already on the target build will damage the
1015 # system. Because operations like move don't check the block state, they
1016 # always apply the changes unconditionally.
1017 if blockimgdiff_version <= 2:
1018 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001019 script.AssertSomeFingerprint(source_fp)
1020 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001021 script.AssertSomeThumbprint(
1022 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001023
1024 else: # blockimgdiff_version > 2
1025 if source_oem_props is None and target_oem_props is None:
1026 script.AssertSomeFingerprint(source_fp, target_fp)
1027 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001028 script.AssertSomeThumbprint(
1029 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1030 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001031 elif source_oem_props is None and target_oem_props is not None:
1032 script.AssertFingerprintOrThumbprint(
1033 source_fp,
1034 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1035 else:
1036 script.AssertFingerprintOrThumbprint(
1037 target_fp,
1038 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001039
Tao Baod8d14be2016-02-04 14:26:02 -08001040 # Check the required cache size (i.e. stashed blocks).
1041 size = []
1042 if system_diff:
1043 size.append(system_diff.required_cache)
1044 if vendor_diff:
1045 size.append(vendor_diff.required_cache)
1046
Geremy Condra36bd3652014-02-06 19:45:10 -08001047 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001048 boot_type, boot_device = common.GetTypeAndDevice(
1049 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001050 d = common.Difference(target_boot, source_boot)
1051 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001052 if d is None:
1053 include_full_boot = True
1054 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1055 else:
1056 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001057
Tao Bao89fbb0f2017-01-10 10:47:58 -08001058 print("boot target: %d source: %d diff: %d" % (
1059 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001060
Doug Zongkerf8340082014-08-05 10:39:37 -07001061 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001062
Doug Zongkerf8340082014-08-05 10:39:37 -07001063 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1064 (boot_type, boot_device,
1065 source_boot.size, source_boot.sha1,
1066 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001067 size.append(target_boot.size)
1068
1069 if size:
1070 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001071
1072 device_specific.IncrementalOTA_VerifyEnd()
1073
1074 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001075 # Stage 1/3: (b) Write recovery image to /boot.
1076 _WriteRecoveryImageToBoot(script, output_zip)
1077
Geremy Condra36bd3652014-02-06 19:45:10 -08001078 script.AppendExtra("""
1079set_stage("%(bcb_dev)s", "2/3");
1080reboot_now("%(bcb_dev)s", "");
1081else
1082""" % bcb_dev)
1083
Tao Baod42e97e2016-11-30 12:11:57 -08001084 # Stage 3/3: Make changes.
1085 script.Comment("Stage 3/3")
1086
Jesse Zhao75bcea02015-01-06 10:59:53 -08001087 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001088 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001089 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001090 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001091
Geremy Condra36bd3652014-02-06 19:45:10 -08001092 script.Comment("---- start making changes here ----")
1093
1094 device_specific.IncrementalOTA_InstallBegin()
1095
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001096 system_diff.WriteScript(script, output_zip,
1097 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001098
Doug Zongkerfc44a512014-08-26 13:10:25 -07001099 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001100 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001101
1102 if OPTIONS.two_step:
1103 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1104 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001105 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001106
1107 if not OPTIONS.two_step:
1108 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001109 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001110 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001111 script.Print("Installing boot image...")
1112 script.WriteRawImage("/boot", "boot.img")
1113 else:
1114 # Produce the boot image by applying a patch to the current
1115 # contents of the boot partition, and write it back to the
1116 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001117 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001118 script.Print("Patching boot image...")
1119 script.ShowProgress(0.1, 10)
1120 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1121 % (boot_type, boot_device,
1122 source_boot.size, source_boot.sha1,
1123 target_boot.size, target_boot.sha1),
1124 "-",
1125 target_boot.size, target_boot.sha1,
1126 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001127 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001128 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001129
1130 # Do device-specific installation (eg, write radio image).
1131 device_specific.IncrementalOTA_InstallEnd()
1132
1133 if OPTIONS.extra_script is not None:
1134 script.AppendExtra(OPTIONS.extra_script)
1135
Doug Zongker922206e2014-03-04 13:16:24 -08001136 if OPTIONS.wipe_user_data:
1137 script.Print("Erasing user data...")
1138 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001139 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001140
Geremy Condra36bd3652014-02-06 19:45:10 -08001141 if OPTIONS.two_step:
1142 script.AppendExtra("""
1143set_stage("%(bcb_dev)s", "");
1144endif;
1145endif;
1146""" % bcb_dev)
1147
1148 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001149 # For downgrade OTAs, we prefer to use the update-binary in the source
1150 # build that is actually newer than the one in the target build.
1151 if OPTIONS.downgrade:
1152 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1153 else:
1154 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001155 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001156 WriteMetadata(metadata, output_zip)
1157
Doug Zongker32b527d2014-03-04 10:03:02 -08001158
Tao Bao9bc6bb22015-11-09 16:58:28 -08001159def WriteVerifyPackage(input_zip, output_zip):
1160 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1161
1162 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1163 recovery_mount_options = OPTIONS.info_dict.get(
1164 "recovery_mount_options")
1165 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001166 if oem_props:
Tao Bao9bc6bb22015-11-09 16:58:28 -08001167 if OPTIONS.oem_source is None:
1168 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -08001169 if not OPTIONS.oem_no_mount:
1170 script.Mount("/oem", recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001171 oem_dict = common.LoadDictionaryFromLines(
1172 open(OPTIONS.oem_source).readlines())
1173
1174 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1175 metadata = {
1176 "post-build": target_fp,
1177 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1178 OPTIONS.info_dict),
1179 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1180 }
1181
1182 device_specific = common.DeviceSpecificParams(
1183 input_zip=input_zip,
1184 input_version=OPTIONS.info_dict["recovery_api_version"],
1185 output_zip=output_zip,
1186 script=script,
1187 input_tmp=OPTIONS.input_tmp,
1188 metadata=metadata,
1189 info_dict=OPTIONS.info_dict)
1190
1191 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1192
1193 script.Print("Verifying device images against %s..." % target_fp)
1194 script.AppendExtra("")
1195
1196 script.Print("Verifying boot...")
1197 boot_img = common.GetBootableImage(
1198 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1199 boot_type, boot_device = common.GetTypeAndDevice(
1200 "/boot", OPTIONS.info_dict)
1201 script.Verify("%s:%s:%d:%s" % (
1202 boot_type, boot_device, boot_img.size, boot_img.sha1))
1203 script.AppendExtra("")
1204
1205 script.Print("Verifying recovery...")
1206 recovery_img = common.GetBootableImage(
1207 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1208 recovery_type, recovery_device = common.GetTypeAndDevice(
1209 "/recovery", OPTIONS.info_dict)
1210 script.Verify("%s:%s:%d:%s" % (
1211 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1212 script.AppendExtra("")
1213
1214 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1215 system_tgt.ResetFileMap()
1216 system_diff = common.BlockDifference("system", system_tgt, src=None)
1217 system_diff.WriteStrictVerifyScript(script)
1218
1219 if HasVendorPartition(input_zip):
1220 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1221 vendor_tgt.ResetFileMap()
1222 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1223 vendor_diff.WriteStrictVerifyScript(script)
1224
1225 # Device specific partitions, such as radio, bootloader and etc.
1226 device_specific.VerifyOTA_Assertions()
1227
1228 script.SetProgress(1.0)
1229 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001230 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001231 WriteMetadata(metadata, output_zip)
1232
1233
Tao Baoc098e9e2016-01-07 13:03:56 -08001234def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1235 source_file=None):
1236 """Generate an Android OTA package that has A/B update payload."""
1237
Tao Bao2dd1c482017-02-03 16:49:39 -08001238 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1239 expected_length=None):
1240 """Compute the streaming metadata for a given zip.
1241
1242 When 'reserve_space' is True, we reserve extra space for the offset and
1243 length of the metadata entry itself, although we don't know the final
1244 values until the package gets signed. This function will be called again
1245 after signing. We then write the actual values and pad the string to the
1246 length we set earlier. Note that we can't use the actual length of the
1247 metadata entry in the second run. Otherwise the offsets for other entries
1248 will be changing again.
1249 """
Tao Baoc96316c2017-01-24 22:10:49 -08001250
1251 def ComputeEntryOffsetSize(name):
1252 """Compute the zip entry offset and size."""
1253 info = zip_file.getinfo(name)
1254 offset = info.header_offset + len(info.FileHeader())
1255 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001256 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001257
1258 # payload.bin and payload_properties.txt must exist.
1259 offsets = [ComputeEntryOffsetSize('payload.bin'),
1260 ComputeEntryOffsetSize('payload_properties.txt')]
1261
1262 # care_map.txt is available only if dm-verity is enabled.
1263 if 'care_map.txt' in zip_file.namelist():
1264 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001265
1266 # 'META-INF/com/android/metadata' is required. We don't know its actual
1267 # offset and length (as well as the values for other entries). So we
1268 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1269 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1270 # beginning of the zip), as well as the possible value changes in other
1271 # entries.
1272 if reserve_space:
1273 offsets.append('metadata:' + ' ' * 10)
1274 else:
1275 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1276
1277 value = ','.join(offsets)
1278 if expected_length is not None:
1279 assert len(value) <= expected_length, \
1280 'Insufficient reserved space: reserved=%d, actual=%d' % (
1281 expected_length, len(value))
1282 value += ' ' * (expected_length - len(value))
1283 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001284
Alex Deymod8d96ec2016-06-10 16:38:31 -07001285 # The place where the output from the subprocess should go.
1286 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1287
Tao Baoc098e9e2016-01-07 13:03:56 -08001288 # Setup signing keys.
1289 if OPTIONS.package_key is None:
1290 OPTIONS.package_key = OPTIONS.info_dict.get(
1291 "default_system_dev_certificate",
1292 "build/target/product/security/testkey")
1293
Tao Baodea0f8b2016-06-20 17:55:06 -07001294 # A/B updater expects a signing key in RSA format. Gets the key ready for
1295 # later use in step 3, unless a payload_signer has been specified.
1296 if OPTIONS.payload_signer is None:
1297 cmd = ["openssl", "pkcs8",
1298 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1299 "-inform", "DER", "-nocrypt"]
1300 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1301 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001302 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1303 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001304 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001305
Tao Baodea0f8b2016-06-20 17:55:06 -07001306 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001307 temp_zip_file = tempfile.NamedTemporaryFile()
1308 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1309 compression=zipfile.ZIP_DEFLATED)
1310
1311 # Metadata to comply with Android OTA package format.
1312 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1313 oem_dict = None
1314 if oem_props:
1315 if OPTIONS.oem_source is None:
1316 raise common.ExternalError("OEM source required for this build")
1317 oem_dict = common.LoadDictionaryFromLines(
1318 open(OPTIONS.oem_source).readlines())
1319
1320 metadata = {
1321 "post-build": CalculateFingerprint(oem_props, oem_dict,
1322 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001323 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1324 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001325 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1326 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001327 "ota-required-cache": "0",
1328 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001329 }
1330
1331 if source_file is not None:
1332 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1333 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001334 metadata["pre-build-incremental"] = GetBuildProp(
1335 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001336
Tao Baob31892e2017-02-07 11:21:17 -08001337 HandleDowngradeMetadata(metadata)
1338 else:
1339 metadata["post-timestamp"] = GetBuildProp(
1340 "ro.build.date.utc", OPTIONS.info_dict)
1341
Tao Baoc098e9e2016-01-07 13:03:56 -08001342 # 1. Generate payload.
1343 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1344 cmd = ["brillo_update_payload", "generate",
1345 "--payload", payload_file,
1346 "--target_image", target_file]
1347 if source_file is not None:
1348 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001349 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1350 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001351 assert p1.returncode == 0, "brillo_update_payload generate failed"
1352
1353 # 2. Generate hashes of the payload and metadata files.
1354 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1355 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1356 cmd = ["brillo_update_payload", "hash",
1357 "--unsigned_payload", payload_file,
1358 "--signature_size", "256",
1359 "--metadata_hash_file", metadata_sig_file,
1360 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001361 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1362 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001363 assert p1.returncode == 0, "brillo_update_payload hash failed"
1364
1365 # 3. Sign the hashes and insert them back into the payload file.
1366 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1367 suffix=".bin")
1368 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1369 suffix=".bin")
1370 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001371 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001372 cmd = [OPTIONS.payload_signer]
1373 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001374 else:
1375 cmd = ["openssl", "pkeyutl", "-sign",
1376 "-inkey", rsa_key,
1377 "-pkeyopt", "digest:sha256"]
1378 cmd.extend(["-in", payload_sig_file,
1379 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001380 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1381 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001382 assert p1.returncode == 0, "openssl sign payload failed"
1383
1384 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001385 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001386 cmd = [OPTIONS.payload_signer]
1387 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001388 else:
1389 cmd = ["openssl", "pkeyutl", "-sign",
1390 "-inkey", rsa_key,
1391 "-pkeyopt", "digest:sha256"]
1392 cmd.extend(["-in", metadata_sig_file,
1393 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001394 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1395 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001396 assert p1.returncode == 0, "openssl sign metadata failed"
1397
1398 # 3c. Insert the signatures back into the payload file.
1399 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1400 suffix=".bin")
1401 cmd = ["brillo_update_payload", "sign",
1402 "--unsigned_payload", payload_file,
1403 "--payload", signed_payload_file,
1404 "--signature_size", "256",
1405 "--metadata_signature_file", signed_metadata_sig_file,
1406 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001407 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1408 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001409 assert p1.returncode == 0, "brillo_update_payload sign failed"
1410
Alex Deymo19241c12016-02-04 22:29:29 -08001411 # 4. Dump the signed payload properties.
1412 properties_file = common.MakeTempFile(prefix="payload-properties-",
1413 suffix=".txt")
1414 cmd = ["brillo_update_payload", "properties",
1415 "--payload", signed_payload_file,
1416 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001417 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1418 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001419 assert p1.returncode == 0, "brillo_update_payload properties failed"
1420
Tao Bao7c5dc572016-06-14 17:48:11 -07001421 if OPTIONS.wipe_user_data:
1422 with open(properties_file, "a") as f:
1423 f.write("POWERWASH=1\n")
1424 metadata["ota-wipe"] = "yes"
1425
Tao Baoc96316c2017-01-24 22:10:49 -08001426 # Add the signed payload file and properties into the zip. In order to
1427 # support streaming, we pack payload.bin, payload_properties.txt and
1428 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1429 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001430 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1431 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001432 common.ZipWrite(output_zip, properties_file,
1433 arcname="payload_properties.txt",
1434 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001435
Tianjie Xucfa86222016-03-07 16:31:19 -08001436 # If dm-verity is supported for the device, copy contents of care_map
1437 # into A/B OTA package.
1438 if OPTIONS.info_dict.get("verity") == "true":
1439 target_zip = zipfile.ZipFile(target_file, "r")
1440 care_map_path = "META/care_map.txt"
1441 namelist = target_zip.namelist()
1442 if care_map_path in namelist:
1443 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001444 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1445 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001446 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001447 print("Warning: cannot find care map file in target_file package")
Tianjie Xucfa86222016-03-07 16:31:19 -08001448 common.ZipClose(target_zip)
1449
Tao Bao2dd1c482017-02-03 16:49:39 -08001450 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001451 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001452 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001453 WriteMetadata(metadata, output_zip)
1454 common.ZipClose(output_zip)
1455
Tao Bao2dd1c482017-02-03 16:49:39 -08001456 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1457 # zip entries, as well as padding the entry headers. We do a preliminary
1458 # signing (with an incomplete metadata entry) to allow that to happen. Then
1459 # compute the zip entry offsets, write back the final metadata and do the
1460 # final signing.
1461 prelim_signing = tempfile.NamedTemporaryFile()
1462 SignOutput(temp_zip_file.name, prelim_signing.name)
1463 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001464
Tao Bao2dd1c482017-02-03 16:49:39 -08001465 # Open the signed zip. Compute the final metadata that's needed for streaming.
1466 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1467 compression=zipfile.ZIP_DEFLATED)
1468 expected_length = len(metadata['ota-streaming-property-files'])
1469 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1470 prelim_zip, reserve_space=False, expected_length=expected_length)
1471
1472 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1473 final_signing = tempfile.NamedTemporaryFile()
1474 output_zip = zipfile.ZipFile(final_signing, "w",
1475 compression=zipfile.ZIP_DEFLATED)
1476 for item in prelim_zip.infolist():
1477 if item.filename == METADATA_NAME:
1478 continue
1479
1480 data = prelim_zip.read(item.filename)
1481 out_info = copy.copy(item)
1482 common.ZipWriteStr(output_zip, out_info, data)
1483
1484 # Now write the final metadata entry.
1485 WriteMetadata(metadata, output_zip)
1486 common.ZipClose(prelim_zip)
1487 common.ZipClose(output_zip)
1488
1489 # Re-sign the package after updating the metadata entry.
1490 SignOutput(final_signing.name, output_file)
1491 final_signing.close()
1492
1493 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001494 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001495 actual = metadata['ota-streaming-property-files'].strip()
1496 expected = ComputeStreamingMetadata(output_zip)
1497 assert actual == expected, \
1498 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001499 common.ZipClose(output_zip)
1500
Tao Baoc098e9e2016-01-07 13:03:56 -08001501
Dan Albert8b72aef2015-03-23 19:13:21 -07001502class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001503 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001504 self.deferred_patch_list = None
Tao Bao89fbb0f2017-01-10 10:47:58 -08001505 print("Loading target...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001506 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001507 print("Loading source...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001508 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1509
1510 self.verbatim_targets = verbatim_targets = []
1511 self.patch_list = patch_list = []
1512 diffs = []
1513 self.renames = renames = {}
1514 known_paths = set()
1515 largest_source_size = 0
1516
1517 matching_file_cache = {}
1518 for fn, sf in source_data.items():
1519 assert fn == sf.name
1520 matching_file_cache["path:" + fn] = sf
1521 if fn in target_data.keys():
1522 AddToKnownPaths(fn, known_paths)
1523 # Only allow eligibility for filename/sha matching
1524 # if there isn't a perfect path match.
1525 if target_data.get(sf.name) is None:
1526 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1527 matching_file_cache["sha:" + sf.sha1] = sf
1528
1529 for fn in sorted(target_data.keys()):
1530 tf = target_data[fn]
1531 assert fn == tf.name
1532 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1533 if sf is not None and sf.name != tf.name:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001534 print("File has moved from " + sf.name + " to " + tf.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001535 renames[sf.name] = tf
1536
1537 if sf is None or fn in OPTIONS.require_verbatim:
1538 # This file should be included verbatim
1539 if fn in OPTIONS.prohibit_verbatim:
1540 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Tao Bao89fbb0f2017-01-10 10:47:58 -08001541 print("send", fn, "verbatim")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001542 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001543 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001544 if fn in target_data.keys():
1545 AddToKnownPaths(fn, known_paths)
1546 elif tf.sha1 != sf.sha1:
1547 # File is different; consider sending as a patch
1548 diffs.append(common.Difference(tf, sf))
1549 else:
1550 # Target file data identical to source (may still be renamed)
1551 pass
1552
1553 common.ComputeDifferences(diffs)
1554
1555 for diff in diffs:
1556 tf, sf, d = diff.GetPatch()
1557 path = "/".join(tf.name.split("/")[:-1])
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001558 if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001559 path not in known_paths:
1560 # patch is almost as big as the file; don't bother patching
1561 # or a patch + rename cannot take place due to the target
1562 # directory not existing
1563 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001564 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001565 if sf.name in renames:
1566 del renames[sf.name]
1567 AddToKnownPaths(tf.name, known_paths)
1568 else:
1569 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1570 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1571 largest_source_size = max(largest_source_size, sf.size)
1572
1573 self.largest_source_size = largest_source_size
1574
1575 def EmitVerification(self, script):
1576 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001577 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001578 if tf.name != sf.name:
1579 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1580 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1581 so_far += sf.size
1582 return so_far
1583
Michael Runge63f01de2014-10-28 19:24:19 -07001584 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001585 for fn, _, sha1 in self.verbatim_targets:
1586 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001587 script.FileCheck("/"+fn, sha1)
1588 for tf, _, _, _ in self.patch_list:
1589 script.FileCheck(tf.name, tf.sha1)
1590
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001591 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001592 file_list = ["/" + i[0] for i in self.verbatim_targets]
1593 file_list += ["/" + i for i in self.source_data
1594 if i not in self.target_data and i not in self.renames]
1595 file_list += list(extras)
1596 # Sort the list in descending order, which removes all the files first
1597 # before attempting to remove the folder. (Bug: 22960996)
1598 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001599
1600 def TotalPatchSize(self):
1601 return sum(i[1].size for i in self.patch_list)
1602
1603 def EmitPatches(self, script, total_patch_size, so_far):
1604 self.deferred_patch_list = deferred_patch_list = []
1605 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001606 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001607 if tf.name == "system/build.prop":
1608 deferred_patch_list.append(item)
1609 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001610 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001611 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001612 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1613 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001614 so_far += tf.size
1615 script.SetProgress(so_far / total_patch_size)
1616 return so_far
1617
1618 def EmitDeferredPatches(self, script):
1619 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001620 tf, sf, _, _ = item
1621 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1622 "patch/" + sf.name + ".p")
1623 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001624
1625 def EmitRenames(self, script):
1626 if len(self.renames) > 0:
1627 script.Print("Renaming files...")
1628 for src, tgt in self.renames.iteritems():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001629 print("Renaming " + src + " to " + tgt.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001630 script.RenameFile(src, tgt.name)
1631
1632
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001633def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001634 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1635 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1636
Doug Zongker26e66192014-02-20 13:22:07 -08001637 if (OPTIONS.block_based and
1638 target_has_recovery_patch and
1639 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001640 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1641
Doug Zongker37974732010-09-16 17:44:38 -07001642 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1643 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001644
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001645 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001646 print("WARNING: generating edify script for a source that "
1647 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001648 script = edify_generator.EdifyGenerator(
1649 source_version, OPTIONS.target_info_dict,
1650 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001651
Tao Bao34b47bf2015-06-22 19:17:41 -07001652 recovery_mount_options = OPTIONS.source_info_dict.get(
1653 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001654 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1655 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -07001656 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001657 if source_oem_props or target_oem_props:
Michael Runge6e836112014-04-15 17:40:21 -07001658 if OPTIONS.oem_source is None:
1659 raise common.ExternalError("OEM source required for this build")
Tao Bao1bb5a182016-03-04 09:45:03 -08001660 if not OPTIONS.oem_no_mount:
1661 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001662 oem_dict = common.LoadDictionaryFromLines(
1663 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001664
Dan Albert8b72aef2015-03-23 19:13:21 -07001665 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001666 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
1667 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001668 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001669 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001670
Tao Baob31892e2017-02-07 11:21:17 -08001671 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -08001672
Doug Zongker05d3dea2009-06-22 11:32:31 -07001673 device_specific = common.DeviceSpecificParams(
1674 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001675 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001676 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001677 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001678 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001679 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001680 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001681 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001682
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001683 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001684 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001685 if HasVendorPartition(target_zip):
1686 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001687 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001688 else:
1689 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001690
Tao Bao3e30d972016-03-15 13:20:19 -07001691 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001692 OPTIONS.target_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -07001693 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001694 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001695
Tao Bao3e30d972016-03-15 13:20:19 -07001696 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001697 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001698 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001699 script.AssertSomeThumbprint(
1700 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1701 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001702 elif source_oem_props is None and target_oem_props is not None:
1703 script.AssertFingerprintOrThumbprint(
1704 source_fp,
1705 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1706 else:
1707 script.AssertFingerprintOrThumbprint(
1708 target_fp,
1709 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001710
Doug Zongker2ea21062010-04-28 16:05:21 -07001711 metadata["pre-build"] = source_fp
1712 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001713 metadata["pre-build-incremental"] = GetBuildProp(
1714 "ro.build.version.incremental", OPTIONS.source_info_dict)
1715 metadata["post-build-incremental"] = GetBuildProp(
1716 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001717
Doug Zongker55d93282011-01-25 17:03:34 -08001718 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001719 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1720 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001721 target_boot = common.GetBootableImage(
1722 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001723 updating_boot = (not OPTIONS.two_step and
1724 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001725
Doug Zongker55d93282011-01-25 17:03:34 -08001726 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001727 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1728 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001729 target_recovery = common.GetBootableImage(
1730 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001731 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001732
Doug Zongker881dd402009-09-20 14:03:55 -07001733 # Here's how we divide up the progress bar:
1734 # 0.1 for verifying the start state (PatchCheck calls)
1735 # 0.8 for applying patches (ApplyPatch calls)
1736 # 0.1 for unpacking verbatim files, symlinking, and doing the
1737 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001738
Michael Runge6e836112014-04-15 17:40:21 -07001739 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001740 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001741
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001742 # Two-step incremental package strategy (in chronological order,
1743 # which is *not* the order in which the generated script has
1744 # things):
1745 #
1746 # if stage is not "2/3" or "3/3":
1747 # do verification on current system
1748 # write recovery image to boot partition
1749 # set stage to "2/3"
1750 # reboot to boot partition and restart recovery
1751 # else if stage is "2/3":
1752 # write recovery image to recovery partition
1753 # set stage to "3/3"
1754 # reboot to recovery partition and restart recovery
1755 # else:
1756 # (stage must be "3/3")
1757 # perform update:
1758 # patch system files, etc.
1759 # force full install of new boot image
1760 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001761 # complete script normally
1762 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001763
1764 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001765 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001766 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001767 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001768 assert fs.fs_type.upper() == "EMMC", \
1769 "two-step packages only supported on devices with EMMC /misc partitions"
1770 bcb_dev = {"bcb_dev": fs.device}
1771 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1772 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001773if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001774""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001775
1776 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1777 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001778 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001779 script.WriteRawImage("/recovery", "recovery.img")
1780 script.AppendExtra("""
1781set_stage("%(bcb_dev)s", "3/3");
1782reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001783else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001784""" % bcb_dev)
1785
Tao Baod42e97e2016-11-30 12:11:57 -08001786 # Stage 1/3: (a) Verify the current system.
1787 script.Comment("Stage 1/3")
1788
Tao Bao6c55a8a2015-04-08 15:30:27 -07001789 # Dump fingerprints
1790 script.Print("Source: %s" % (source_fp,))
1791 script.Print("Target: %s" % (target_fp,))
1792
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001793 script.Print("Verifying current system...")
1794
Doug Zongkere5ff5902012-01-17 10:55:37 -08001795 device_specific.IncrementalOTA_VerifyBegin()
1796
Doug Zongker881dd402009-09-20 14:03:55 -07001797 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001798 so_far = system_diff.EmitVerification(script)
1799 if vendor_diff:
1800 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001801
Tao Baod8d14be2016-02-04 14:26:02 -08001802 size = []
1803 if system_diff.patch_list:
1804 size.append(system_diff.largest_source_size)
1805 if vendor_diff:
1806 if vendor_diff.patch_list:
1807 size.append(vendor_diff.largest_source_size)
1808
Doug Zongker5da317e2009-06-02 13:38:17 -07001809 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001810 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001811 _, _, d = d.ComputePatch()
Tao Bao89fbb0f2017-01-10 10:47:58 -08001812 print("boot target: %d source: %d diff: %d" % (
1813 target_boot.size, source_boot.size, len(d)))
Doug Zongker5da317e2009-06-02 13:38:17 -07001814
Doug Zongker048e7ca2009-06-15 14:31:53 -07001815 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001816
Tao Baodd24da92015-07-29 14:09:23 -07001817 boot_type, boot_device = common.GetTypeAndDevice(
1818 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001819
1820 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1821 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001822 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001823 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001824 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001825 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001826
Tao Baod8d14be2016-02-04 14:26:02 -08001827 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001828 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001829
Doug Zongker05d3dea2009-06-22 11:32:31 -07001830 device_specific.IncrementalOTA_VerifyEnd()
1831
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001832 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001833 # Stage 1/3: (b) Write recovery image to /boot.
1834 _WriteRecoveryImageToBoot(script, output_zip)
1835
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001836 script.AppendExtra("""
1837set_stage("%(bcb_dev)s", "2/3");
1838reboot_now("%(bcb_dev)s", "");
1839else
1840""" % bcb_dev)
1841
Tao Baod42e97e2016-11-30 12:11:57 -08001842 # Stage 3/3: Make changes.
1843 script.Comment("Stage 3/3")
1844
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001845 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001846
Doug Zongkere5ff5902012-01-17 10:55:37 -08001847 device_specific.IncrementalOTA_InstallBegin()
1848
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001849 if OPTIONS.two_step:
1850 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1851 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001852 print("writing full boot image (forced by two-step mode)")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001853
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001854 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001855 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1856 if vendor_diff:
1857 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001858
Doug Zongker881dd402009-09-20 14:03:55 -07001859 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001860 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1861 if vendor_diff:
1862 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001863 if updating_boot:
1864 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001865
1866 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001867 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1868 if vendor_diff:
1869 script.Print("Patching vendor files...")
1870 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001871
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001872 if not OPTIONS.two_step:
1873 if updating_boot:
1874 # Produce the boot image by applying a patch to the current
1875 # contents of the boot partition, and write it back to the
1876 # partition.
1877 script.Print("Patching boot image...")
1878 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1879 % (boot_type, boot_device,
1880 source_boot.size, source_boot.sha1,
1881 target_boot.size, target_boot.sha1),
1882 "-",
1883 target_boot.size, target_boot.sha1,
1884 source_boot.sha1, "patch/boot.img.p")
1885 so_far += target_boot.size
1886 script.SetProgress(so_far / total_patch_size)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001887 print("boot image changed; including.")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001888 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001889 print("boot image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001890
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001891 system_items = ItemSet("system", "META/filesystem_config.txt")
1892 if vendor_diff:
1893 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1894
Doug Zongkereef39442009-04-02 12:14:19 -07001895 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001896 # Recovery is generated as a patch using both the boot image
1897 # (which contains the same linux kernel as recovery) and the file
1898 # /system/etc/recovery-resource.dat (which contains all the images
1899 # used in the recovery UI) as sources. This lets us minimize the
1900 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001901 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001902 # For older builds where recovery-resource.dat is not present, we
1903 # use only the boot image as the source.
1904
Doug Zongkerc9253822014-02-04 12:17:58 -08001905 if not target_has_recovery_patch:
1906 def output_sink(fn, data):
1907 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001908 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001909
1910 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1911 target_recovery, target_boot)
1912 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001913 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001914 "/system/etc/install-recovery.sh"])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001915 print("recovery image changed; including as patch from boot.")
Doug Zongkereef39442009-04-02 12:14:19 -07001916 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001917 print("recovery image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001918
Doug Zongker881dd402009-09-20 14:03:55 -07001919 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001920
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001921 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1922 if vendor_diff:
1923 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1924
1925 temp_script = script.MakeTemporary()
1926 system_items.GetMetadata(target_zip)
1927 system_items.Get("system").SetPermissions(temp_script)
1928 if vendor_diff:
1929 vendor_items.GetMetadata(target_zip)
1930 vendor_items.Get("vendor").SetPermissions(temp_script)
1931
1932 # Note that this call will mess up the trees of Items, so make sure
1933 # we're done with them.
1934 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1935 if vendor_diff:
1936 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001937
1938 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001939 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1940
1941 # Delete all the symlinks in source that aren't in target. This
1942 # needs to happen before verbatim files are unpacked, in case a
1943 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001944
1945 # If a symlink in the source will be replaced by a regular file, we cannot
1946 # delete the symlink/file in case the package gets applied again. For such
1947 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1948 # (Bug: 23646151)
1949 replaced_symlinks = dict()
1950 if system_diff:
1951 for i in system_diff.verbatim_targets:
1952 replaced_symlinks["/%s" % (i[0],)] = i[2]
1953 if vendor_diff:
1954 for i in vendor_diff.verbatim_targets:
1955 replaced_symlinks["/%s" % (i[0],)] = i[2]
1956
1957 if system_diff:
1958 for tf in system_diff.renames.values():
1959 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1960 if vendor_diff:
1961 for tf in vendor_diff.renames.values():
1962 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1963
1964 always_delete = []
1965 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001966 for dest, link in source_symlinks:
1967 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001968 if link in replaced_symlinks:
1969 may_delete.append((link, replaced_symlinks[link]))
1970 else:
1971 always_delete.append(link)
1972 script.DeleteFiles(always_delete)
1973 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001974
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001975 if system_diff.verbatim_targets:
1976 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001977 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001978 if vendor_diff and vendor_diff.verbatim_targets:
1979 script.Print("Unpacking new vendor files...")
1980 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001981
Doug Zongkerc9253822014-02-04 12:17:58 -08001982 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001983 script.Print("Unpacking new recovery...")
1984 script.UnpackPackageDir("recovery", "/system")
1985
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001986 system_diff.EmitRenames(script)
1987 if vendor_diff:
1988 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001989
Doug Zongker05d3dea2009-06-22 11:32:31 -07001990 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001991
1992 # Create all the symlinks that don't already exist, or point to
1993 # somewhere different than what we want. Delete each symlink before
1994 # creating it, since the 'symlink' command won't overwrite.
1995 to_create = []
1996 for dest, link in target_symlinks:
1997 if link in source_symlinks_d:
1998 if dest != source_symlinks_d[link]:
1999 to_create.append((dest, link))
2000 else:
2001 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002002 script.DeleteFiles([i[1] for i in to_create])
2003 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07002004
2005 # Now that the symlinks are created, we can set all the
2006 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002007 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07002008
Doug Zongker881dd402009-09-20 14:03:55 -07002009 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07002010 device_specific.IncrementalOTA_InstallEnd()
2011
Doug Zongker1c390a22009-05-14 19:06:36 -07002012 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07002013 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07002014
Doug Zongkere92f15a2011-08-26 13:46:40 -07002015 # Patch the build.prop file last, so if something fails but the
2016 # device can still come up, it appears to be the old build and will
2017 # get set the OTA package again to retry.
2018 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07002019 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07002020
Doug Zongker922206e2014-03-04 13:16:24 -08002021 if OPTIONS.wipe_user_data:
2022 script.Print("Erasing user data...")
2023 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08002024 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08002025
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002026 if OPTIONS.two_step:
2027 script.AppendExtra("""
2028set_stage("%(bcb_dev)s", "");
2029endif;
2030endif;
2031""" % bcb_dev)
2032
Michael Runge63f01de2014-10-28 19:24:19 -07002033 if OPTIONS.verify and system_diff:
2034 script.Print("Remounting and verifying system partition files...")
2035 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08002036 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002037 system_diff.EmitExplicitTargetVerification(script)
2038
2039 if OPTIONS.verify and vendor_diff:
2040 script.Print("Remounting and verifying vendor partition files...")
2041 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08002042 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002043 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08002044
2045 # For downgrade OTAs, we prefer to use the update-binary in the source
2046 # build that is actually newer than the one in the target build.
2047 if OPTIONS.downgrade:
2048 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
2049 else:
2050 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07002051
Tao Baod8d14be2016-02-04 14:26:02 -08002052 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07002053 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07002054
2055
2056def main(argv):
2057
2058 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08002059 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002060 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07002061 elif o in ("-k", "--package_key"):
2062 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002063 elif o in ("-i", "--incremental_from"):
2064 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002065 elif o == "--full_radio":
2066 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002067 elif o == "--full_bootloader":
2068 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002069 elif o in ("-w", "--wipe_user_data"):
2070 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002071 elif o == "--downgrade":
2072 OPTIONS.downgrade = True
2073 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07002074 elif o in ("-o", "--oem_settings"):
2075 OPTIONS.oem_source = a
Tao Bao8608cde2016-02-25 19:49:55 -08002076 elif o == "--oem_no_mount":
2077 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002078 elif o in ("-e", "--extra_script"):
2079 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002080 elif o in ("-t", "--worker_threads"):
2081 if a.isdigit():
2082 OPTIONS.worker_threads = int(a)
2083 else:
2084 raise ValueError("Cannot parse value %r for option %r - only "
2085 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002086 elif o in ("-2", "--two_step"):
2087 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002088 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002089 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002090 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002091 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002092 elif o == "--block":
2093 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002094 elif o in ("-b", "--binary"):
2095 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002096 elif o in ("--no_fallback_to_full",):
2097 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002098 elif o == "--stash_threshold":
2099 try:
2100 OPTIONS.stash_threshold = float(a)
2101 except ValueError:
2102 raise ValueError("Cannot parse value %r for option %r - expecting "
2103 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002104 elif o == "--gen_verify":
2105 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002106 elif o == "--log_diff":
2107 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002108 elif o == "--payload_signer":
2109 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002110 elif o == "--payload_signer_args":
2111 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002112 else:
2113 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002114 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002115
2116 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08002117 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002118 extra_long_opts=[
2119 "board_config=",
2120 "package_key=",
2121 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002122 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002123 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002124 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002125 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07002126 "extra_script=",
2127 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002128 "two_step",
2129 "no_signing",
2130 "block",
2131 "binary=",
2132 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002133 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002134 "verify",
2135 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002136 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002137 "gen_verify",
2138 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002139 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002140 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002141 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002142
2143 if len(args) != 2:
2144 common.Usage(__doc__)
2145 sys.exit(1)
2146
Tao Bao5d182562016-02-23 11:38:39 -08002147 if OPTIONS.downgrade:
2148 # Sanity check to enforce a data wipe.
2149 if not OPTIONS.wipe_user_data:
2150 raise ValueError("Cannot downgrade without a data wipe")
2151
2152 # We should only allow downgrading incrementals (as opposed to full).
2153 # Otherwise the device may go back from arbitrary build with this full
2154 # OTA package.
2155 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002156 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002157
Tao Baoc098e9e2016-01-07 13:03:56 -08002158 # Load the dict file from the zip directly to have a peek at the OTA type.
2159 # For packages using A/B update, unzipping is not needed.
2160 input_zip = zipfile.ZipFile(args[0], "r")
2161 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2162 common.ZipClose(input_zip)
2163
2164 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2165
2166 if ab_update:
2167 if OPTIONS.incremental_source is not None:
2168 OPTIONS.target_info_dict = OPTIONS.info_dict
2169 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2170 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2171 common.ZipClose(source_zip)
2172
2173 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002174 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002175 common.DumpInfoDict(OPTIONS.info_dict)
2176
2177 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002178 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002179 common.DumpInfoDict(OPTIONS.source_info_dict)
2180
2181 WriteABOTAPackageWithBrilloScript(
2182 target_file=args[0],
2183 output_file=args[1],
2184 source_file=OPTIONS.incremental_source)
2185
Tao Bao89fbb0f2017-01-10 10:47:58 -08002186 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002187 return
2188
Doug Zongker1c390a22009-05-14 19:06:36 -07002189 if OPTIONS.extra_script is not None:
2190 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2191
Tao Bao89fbb0f2017-01-10 10:47:58 -08002192 print("unzipping target target-files...")
Doug Zongker55d93282011-01-25 17:03:34 -08002193 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002194
Doug Zongkereef39442009-04-02 12:14:19 -07002195 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002196 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002197
Doug Zongker37974732010-09-16 17:44:38 -07002198 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002199 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07002200 common.DumpInfoDict(OPTIONS.info_dict)
2201
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002202 # If the caller explicitly specified the device-specific extensions
2203 # path via -s/--device_specific, use that. Otherwise, use
2204 # META/releasetools.py if it is present in the target target_files.
2205 # Otherwise, take the path of the file from 'tool_extensions' in the
2206 # info dict and look for that in the local filesystem, relative to
2207 # the current directory.
2208
Doug Zongker37974732010-09-16 17:44:38 -07002209 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002210 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2211 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08002212 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002213 OPTIONS.device_specific = from_input
2214 else:
2215 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2216
Doug Zongker37974732010-09-16 17:44:38 -07002217 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002218 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002219
Tao Baoc098e9e2016-01-07 13:03:56 -08002220 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002221 raise common.ExternalError(
2222 "--- target build has specified no recovery ---")
2223
Tao Bao767e3ac2015-11-10 12:19:19 -08002224 # Use the default key to sign the package if not specified with package_key.
2225 if not OPTIONS.no_signing:
2226 if OPTIONS.package_key is None:
2227 OPTIONS.package_key = OPTIONS.info_dict.get(
2228 "default_system_dev_certificate",
2229 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002230
Tao Bao767e3ac2015-11-10 12:19:19 -08002231 # Set up the output zip. Create a temporary zip file if signing is needed.
2232 if OPTIONS.no_signing:
2233 if os.path.exists(args[1]):
2234 os.unlink(args[1])
2235 output_zip = zipfile.ZipFile(args[1], "w",
2236 compression=zipfile.ZIP_DEFLATED)
2237 else:
2238 temp_zip_file = tempfile.NamedTemporaryFile()
2239 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2240 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002241
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002242 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002243 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002244 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002245 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002246 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002247
Tao Bao9bc6bb22015-11-09 16:58:28 -08002248 # Generate a verify package.
2249 if OPTIONS.gen_verify:
2250 WriteVerifyPackage(input_zip, output_zip)
2251
Tao Bao767e3ac2015-11-10 12:19:19 -08002252 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002253 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002254 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002255
2256 # Generate an incremental OTA. It will fall back to generate a full OTA on
2257 # failure unless no_fallback_to_full is specified.
2258 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002259 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08002260 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2261 OPTIONS.incremental_source)
2262 OPTIONS.target_info_dict = OPTIONS.info_dict
2263 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2264 OPTIONS.source_tmp)
2265 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002266 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002267 common.DumpInfoDict(OPTIONS.source_info_dict)
2268 try:
2269 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002270 if OPTIONS.log_diff:
2271 out_file = open(OPTIONS.log_diff, 'w')
2272 import target_files_diff
2273 target_files_diff.recursiveDiff('',
2274 OPTIONS.source_tmp,
2275 OPTIONS.input_tmp,
2276 out_file)
2277 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002278 except ValueError:
2279 if not OPTIONS.fallback_to_full:
2280 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08002281 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002282 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002283 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002284
Tao Bao767e3ac2015-11-10 12:19:19 -08002285 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002286
Tao Bao767e3ac2015-11-10 12:19:19 -08002287 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002288 if not OPTIONS.no_signing:
2289 SignOutput(temp_zip_file.name, args[1])
2290 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002291
Tao Bao89fbb0f2017-01-10 10:47:58 -08002292 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002293
2294
2295if __name__ == '__main__':
2296 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002297 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002298 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002299 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002300 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002301 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002302 finally:
2303 common.Cleanup()