blob: c04c785524afa8407c80603072dc82c94e94962d [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
Geremy Condra36bd3652014-02-06 19:45:10 -0800818def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700819 # TODO(tbao): We should factor out the common parts between
820 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800821 source_version = OPTIONS.source_info_dict["recovery_api_version"]
822 target_version = OPTIONS.target_info_dict["recovery_api_version"]
823
824 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700825 print("WARNING: generating edify script for a source that "
826 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700827 script = edify_generator.EdifyGenerator(
828 source_version, OPTIONS.target_info_dict,
829 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800830
Tao Bao3806c232015-07-05 21:08:33 -0700831 recovery_mount_options = OPTIONS.source_info_dict.get(
832 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700833 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
834 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Tao Bao3806c232015-07-05 21:08:33 -0700835 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700836 if source_oem_props or target_oem_props:
Tao Bao3806c232015-07-05 21:08:33 -0700837 if OPTIONS.oem_source is None:
838 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800839 if not OPTIONS.oem_no_mount:
840 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700841 oem_dict = common.LoadDictionaryFromLines(
842 open(OPTIONS.oem_source).readlines())
843
Dan Albert8b72aef2015-03-23 19:13:21 -0700844 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700845 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
846 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800847 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700848 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800849
Tao Bao5d182562016-02-23 11:38:39 -0800850 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
851 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
852 is_downgrade = long(post_timestamp) < long(pre_timestamp)
853
854 if OPTIONS.downgrade:
855 metadata["ota-downgrade"] = "yes"
856 if not is_downgrade:
857 raise RuntimeError("--downgrade specified but no downgrade detected: "
858 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
859 else:
860 if is_downgrade:
861 # Non-fatal here to allow generating such a package which may require
862 # manual work to adjust the post-timestamp. A legit use case is that we
863 # cut a new build C (after having A and B), but want to enfore the
864 # update path of A -> C -> B. Specifying --downgrade may not help since
865 # that would enforce a data wipe for C -> B update.
866 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
867 "The package may not be deployed properly. "
868 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
869 metadata["post-timestamp"] = post_timestamp
870
Geremy Condra36bd3652014-02-06 19:45:10 -0800871 device_specific = common.DeviceSpecificParams(
872 source_zip=source_zip,
873 source_version=source_version,
874 target_zip=target_zip,
875 target_version=target_version,
876 output_zip=output_zip,
877 script=script,
878 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700879 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800880
Tao Bao3e30d972016-03-15 13:20:19 -0700881 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700882 OPTIONS.source_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -0700883 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700884 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800885 metadata["pre-build"] = source_fp
886 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700887 metadata["pre-build-incremental"] = GetBuildProp(
888 "ro.build.version.incremental", OPTIONS.source_info_dict)
889 metadata["post-build-incremental"] = GetBuildProp(
890 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800891
892 source_boot = common.GetBootableImage(
893 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
894 OPTIONS.source_info_dict)
895 target_boot = common.GetBootableImage(
896 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
897 updating_boot = (not OPTIONS.two_step and
898 (source_boot.data != target_boot.data))
899
Geremy Condra36bd3652014-02-06 19:45:10 -0800900 target_recovery = common.GetBootableImage(
901 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800902
Doug Zongkerfc44a512014-08-26 13:10:25 -0700903 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
904 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700905
906 blockimgdiff_version = 1
907 if OPTIONS.info_dict:
908 blockimgdiff_version = max(
909 int(i) for i in
910 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
911
Tao Baof8acad12016-07-07 09:09:58 -0700912 # Check the first block of the source system partition for remount R/W only
913 # if the filesystem is ext4.
914 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
915 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700916 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
917 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
918 # b) the blocks listed in block map may not contain all the bytes for a given
919 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700920 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
921 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
922 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700923 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800924 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700925 version=blockimgdiff_version,
926 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700927
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700928 if HasVendorPartition(target_zip):
929 if not HasVendorPartition(source_zip):
930 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700931 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
932 OPTIONS.source_info_dict)
933 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
934 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800935
936 # Check first block of vendor partition for remount R/W only if
937 # disk type is ext4
938 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800939 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700940 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700941 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800942 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700943 version=blockimgdiff_version,
944 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700945 else:
946 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800947
Michael Rungec6e3afd2014-05-05 11:55:47 -0700948 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800949 device_specific.IncrementalOTA_Assertions()
950
951 # Two-step incremental package strategy (in chronological order,
952 # which is *not* the order in which the generated script has
953 # things):
954 #
955 # if stage is not "2/3" or "3/3":
956 # do verification on current system
957 # write recovery image to boot partition
958 # set stage to "2/3"
959 # reboot to boot partition and restart recovery
960 # else if stage is "2/3":
961 # write recovery image to recovery partition
962 # set stage to "3/3"
963 # reboot to recovery partition and restart recovery
964 # else:
965 # (stage must be "3/3")
966 # perform update:
967 # patch system files, etc.
968 # force full install of new boot image
969 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700970 # complete script normally
971 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800972
973 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700974 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800975 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700976 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800977 assert fs.fs_type.upper() == "EMMC", \
978 "two-step packages only supported on devices with EMMC /misc partitions"
979 bcb_dev = {"bcb_dev": fs.device}
980 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
981 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700982if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800983""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800984
985 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
986 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700987 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800988 script.WriteRawImage("/recovery", "recovery.img")
989 script.AppendExtra("""
990set_stage("%(bcb_dev)s", "3/3");
991reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700992else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800993""" % bcb_dev)
994
Tao Baod42e97e2016-11-30 12:11:57 -0800995 # Stage 1/3: (a) Verify the current system.
996 script.Comment("Stage 1/3")
997
Tao Bao6c55a8a2015-04-08 15:30:27 -0700998 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -0800999 script.Print("Source: %s" % (source_fp,))
1000 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001001
Geremy Condra36bd3652014-02-06 19:45:10 -08001002 script.Print("Verifying current system...")
1003
1004 device_specific.IncrementalOTA_VerifyBegin()
1005
Tao Bao3e30d972016-03-15 13:20:19 -07001006 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1007 # patching on a device that's already on the target build will damage the
1008 # system. Because operations like move don't check the block state, they
1009 # always apply the changes unconditionally.
1010 if blockimgdiff_version <= 2:
1011 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001012 script.AssertSomeFingerprint(source_fp)
1013 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001014 script.AssertSomeThumbprint(
1015 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001016
1017 else: # blockimgdiff_version > 2
1018 if source_oem_props is None and target_oem_props is None:
1019 script.AssertSomeFingerprint(source_fp, target_fp)
1020 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001021 script.AssertSomeThumbprint(
1022 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1023 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001024 elif source_oem_props is None and target_oem_props is not None:
1025 script.AssertFingerprintOrThumbprint(
1026 source_fp,
1027 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1028 else:
1029 script.AssertFingerprintOrThumbprint(
1030 target_fp,
1031 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001032
Tao Baod8d14be2016-02-04 14:26:02 -08001033 # Check the required cache size (i.e. stashed blocks).
1034 size = []
1035 if system_diff:
1036 size.append(system_diff.required_cache)
1037 if vendor_diff:
1038 size.append(vendor_diff.required_cache)
1039
Geremy Condra36bd3652014-02-06 19:45:10 -08001040 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001041 boot_type, boot_device = common.GetTypeAndDevice(
1042 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001043 d = common.Difference(target_boot, source_boot)
1044 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001045 if d is None:
1046 include_full_boot = True
1047 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1048 else:
1049 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001050
Tao Bao89fbb0f2017-01-10 10:47:58 -08001051 print("boot target: %d source: %d diff: %d" % (
1052 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001053
Doug Zongkerf8340082014-08-05 10:39:37 -07001054 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001055
Doug Zongkerf8340082014-08-05 10:39:37 -07001056 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1057 (boot_type, boot_device,
1058 source_boot.size, source_boot.sha1,
1059 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001060 size.append(target_boot.size)
1061
1062 if size:
1063 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001064
1065 device_specific.IncrementalOTA_VerifyEnd()
1066
1067 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001068 # Stage 1/3: (b) Write recovery image to /boot.
1069 _WriteRecoveryImageToBoot(script, output_zip)
1070
Geremy Condra36bd3652014-02-06 19:45:10 -08001071 script.AppendExtra("""
1072set_stage("%(bcb_dev)s", "2/3");
1073reboot_now("%(bcb_dev)s", "");
1074else
1075""" % bcb_dev)
1076
Tao Baod42e97e2016-11-30 12:11:57 -08001077 # Stage 3/3: Make changes.
1078 script.Comment("Stage 3/3")
1079
Jesse Zhao75bcea02015-01-06 10:59:53 -08001080 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001081 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001082 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001083 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001084
Geremy Condra36bd3652014-02-06 19:45:10 -08001085 script.Comment("---- start making changes here ----")
1086
1087 device_specific.IncrementalOTA_InstallBegin()
1088
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001089 system_diff.WriteScript(script, output_zip,
1090 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001091
Doug Zongkerfc44a512014-08-26 13:10:25 -07001092 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001093 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001094
1095 if OPTIONS.two_step:
1096 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1097 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001098 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001099
1100 if not OPTIONS.two_step:
1101 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001102 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001103 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001104 script.Print("Installing boot image...")
1105 script.WriteRawImage("/boot", "boot.img")
1106 else:
1107 # Produce the boot image by applying a patch to the current
1108 # contents of the boot partition, and write it back to the
1109 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001110 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001111 script.Print("Patching boot image...")
1112 script.ShowProgress(0.1, 10)
1113 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1114 % (boot_type, boot_device,
1115 source_boot.size, source_boot.sha1,
1116 target_boot.size, target_boot.sha1),
1117 "-",
1118 target_boot.size, target_boot.sha1,
1119 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001120 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001121 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001122
1123 # Do device-specific installation (eg, write radio image).
1124 device_specific.IncrementalOTA_InstallEnd()
1125
1126 if OPTIONS.extra_script is not None:
1127 script.AppendExtra(OPTIONS.extra_script)
1128
Doug Zongker922206e2014-03-04 13:16:24 -08001129 if OPTIONS.wipe_user_data:
1130 script.Print("Erasing user data...")
1131 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001132 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001133
Geremy Condra36bd3652014-02-06 19:45:10 -08001134 if OPTIONS.two_step:
1135 script.AppendExtra("""
1136set_stage("%(bcb_dev)s", "");
1137endif;
1138endif;
1139""" % bcb_dev)
1140
1141 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001142 # For downgrade OTAs, we prefer to use the update-binary in the source
1143 # build that is actually newer than the one in the target build.
1144 if OPTIONS.downgrade:
1145 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1146 else:
1147 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001148 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001149 WriteMetadata(metadata, output_zip)
1150
Doug Zongker32b527d2014-03-04 10:03:02 -08001151
Tao Bao9bc6bb22015-11-09 16:58:28 -08001152def WriteVerifyPackage(input_zip, output_zip):
1153 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1154
1155 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1156 recovery_mount_options = OPTIONS.info_dict.get(
1157 "recovery_mount_options")
1158 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001159 if oem_props:
Tao Bao9bc6bb22015-11-09 16:58:28 -08001160 if OPTIONS.oem_source is None:
1161 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -08001162 if not OPTIONS.oem_no_mount:
1163 script.Mount("/oem", recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001164 oem_dict = common.LoadDictionaryFromLines(
1165 open(OPTIONS.oem_source).readlines())
1166
1167 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1168 metadata = {
1169 "post-build": target_fp,
1170 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1171 OPTIONS.info_dict),
1172 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1173 }
1174
1175 device_specific = common.DeviceSpecificParams(
1176 input_zip=input_zip,
1177 input_version=OPTIONS.info_dict["recovery_api_version"],
1178 output_zip=output_zip,
1179 script=script,
1180 input_tmp=OPTIONS.input_tmp,
1181 metadata=metadata,
1182 info_dict=OPTIONS.info_dict)
1183
1184 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1185
1186 script.Print("Verifying device images against %s..." % target_fp)
1187 script.AppendExtra("")
1188
1189 script.Print("Verifying boot...")
1190 boot_img = common.GetBootableImage(
1191 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1192 boot_type, boot_device = common.GetTypeAndDevice(
1193 "/boot", OPTIONS.info_dict)
1194 script.Verify("%s:%s:%d:%s" % (
1195 boot_type, boot_device, boot_img.size, boot_img.sha1))
1196 script.AppendExtra("")
1197
1198 script.Print("Verifying recovery...")
1199 recovery_img = common.GetBootableImage(
1200 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1201 recovery_type, recovery_device = common.GetTypeAndDevice(
1202 "/recovery", OPTIONS.info_dict)
1203 script.Verify("%s:%s:%d:%s" % (
1204 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1205 script.AppendExtra("")
1206
1207 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1208 system_tgt.ResetFileMap()
1209 system_diff = common.BlockDifference("system", system_tgt, src=None)
1210 system_diff.WriteStrictVerifyScript(script)
1211
1212 if HasVendorPartition(input_zip):
1213 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1214 vendor_tgt.ResetFileMap()
1215 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1216 vendor_diff.WriteStrictVerifyScript(script)
1217
1218 # Device specific partitions, such as radio, bootloader and etc.
1219 device_specific.VerifyOTA_Assertions()
1220
1221 script.SetProgress(1.0)
1222 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001223 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001224 WriteMetadata(metadata, output_zip)
1225
1226
Tao Baoc098e9e2016-01-07 13:03:56 -08001227def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1228 source_file=None):
1229 """Generate an Android OTA package that has A/B update payload."""
1230
Tao Bao2dd1c482017-02-03 16:49:39 -08001231 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1232 expected_length=None):
1233 """Compute the streaming metadata for a given zip.
1234
1235 When 'reserve_space' is True, we reserve extra space for the offset and
1236 length of the metadata entry itself, although we don't know the final
1237 values until the package gets signed. This function will be called again
1238 after signing. We then write the actual values and pad the string to the
1239 length we set earlier. Note that we can't use the actual length of the
1240 metadata entry in the second run. Otherwise the offsets for other entries
1241 will be changing again.
1242 """
Tao Baoc96316c2017-01-24 22:10:49 -08001243
1244 def ComputeEntryOffsetSize(name):
1245 """Compute the zip entry offset and size."""
1246 info = zip_file.getinfo(name)
1247 offset = info.header_offset + len(info.FileHeader())
1248 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001249 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001250
1251 # payload.bin and payload_properties.txt must exist.
1252 offsets = [ComputeEntryOffsetSize('payload.bin'),
1253 ComputeEntryOffsetSize('payload_properties.txt')]
1254
1255 # care_map.txt is available only if dm-verity is enabled.
1256 if 'care_map.txt' in zip_file.namelist():
1257 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001258
1259 # 'META-INF/com/android/metadata' is required. We don't know its actual
1260 # offset and length (as well as the values for other entries). So we
1261 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1262 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1263 # beginning of the zip), as well as the possible value changes in other
1264 # entries.
1265 if reserve_space:
1266 offsets.append('metadata:' + ' ' * 10)
1267 else:
1268 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1269
1270 value = ','.join(offsets)
1271 if expected_length is not None:
1272 assert len(value) <= expected_length, \
1273 'Insufficient reserved space: reserved=%d, actual=%d' % (
1274 expected_length, len(value))
1275 value += ' ' * (expected_length - len(value))
1276 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001277
Alex Deymod8d96ec2016-06-10 16:38:31 -07001278 # The place where the output from the subprocess should go.
1279 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1280
Tao Baoc098e9e2016-01-07 13:03:56 -08001281 # Setup signing keys.
1282 if OPTIONS.package_key is None:
1283 OPTIONS.package_key = OPTIONS.info_dict.get(
1284 "default_system_dev_certificate",
1285 "build/target/product/security/testkey")
1286
Tao Baodea0f8b2016-06-20 17:55:06 -07001287 # A/B updater expects a signing key in RSA format. Gets the key ready for
1288 # later use in step 3, unless a payload_signer has been specified.
1289 if OPTIONS.payload_signer is None:
1290 cmd = ["openssl", "pkcs8",
1291 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1292 "-inform", "DER", "-nocrypt"]
1293 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1294 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001295 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1296 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001297 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001298
Tao Baodea0f8b2016-06-20 17:55:06 -07001299 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001300 temp_zip_file = tempfile.NamedTemporaryFile()
1301 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1302 compression=zipfile.ZIP_DEFLATED)
1303
1304 # Metadata to comply with Android OTA package format.
1305 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1306 oem_dict = None
1307 if oem_props:
1308 if OPTIONS.oem_source is None:
1309 raise common.ExternalError("OEM source required for this build")
1310 oem_dict = common.LoadDictionaryFromLines(
1311 open(OPTIONS.oem_source).readlines())
1312
1313 metadata = {
1314 "post-build": CalculateFingerprint(oem_props, oem_dict,
1315 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001316 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1317 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001318 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1319 OPTIONS.info_dict),
1320 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001321 "ota-required-cache": "0",
1322 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001323 }
1324
1325 if source_file is not None:
1326 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1327 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001328 metadata["pre-build-incremental"] = GetBuildProp(
1329 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001330
1331 # 1. Generate payload.
1332 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1333 cmd = ["brillo_update_payload", "generate",
1334 "--payload", payload_file,
1335 "--target_image", target_file]
1336 if source_file is not None:
1337 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001338 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1339 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001340 assert p1.returncode == 0, "brillo_update_payload generate failed"
1341
1342 # 2. Generate hashes of the payload and metadata files.
1343 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1344 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1345 cmd = ["brillo_update_payload", "hash",
1346 "--unsigned_payload", payload_file,
1347 "--signature_size", "256",
1348 "--metadata_hash_file", metadata_sig_file,
1349 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001350 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1351 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001352 assert p1.returncode == 0, "brillo_update_payload hash failed"
1353
1354 # 3. Sign the hashes and insert them back into the payload file.
1355 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1356 suffix=".bin")
1357 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1358 suffix=".bin")
1359 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001360 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001361 cmd = [OPTIONS.payload_signer]
1362 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001363 else:
1364 cmd = ["openssl", "pkeyutl", "-sign",
1365 "-inkey", rsa_key,
1366 "-pkeyopt", "digest:sha256"]
1367 cmd.extend(["-in", payload_sig_file,
1368 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001369 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1370 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001371 assert p1.returncode == 0, "openssl sign payload failed"
1372
1373 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001374 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001375 cmd = [OPTIONS.payload_signer]
1376 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001377 else:
1378 cmd = ["openssl", "pkeyutl", "-sign",
1379 "-inkey", rsa_key,
1380 "-pkeyopt", "digest:sha256"]
1381 cmd.extend(["-in", metadata_sig_file,
1382 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001383 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1384 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001385 assert p1.returncode == 0, "openssl sign metadata failed"
1386
1387 # 3c. Insert the signatures back into the payload file.
1388 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1389 suffix=".bin")
1390 cmd = ["brillo_update_payload", "sign",
1391 "--unsigned_payload", payload_file,
1392 "--payload", signed_payload_file,
1393 "--signature_size", "256",
1394 "--metadata_signature_file", signed_metadata_sig_file,
1395 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001396 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1397 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001398 assert p1.returncode == 0, "brillo_update_payload sign failed"
1399
Alex Deymo19241c12016-02-04 22:29:29 -08001400 # 4. Dump the signed payload properties.
1401 properties_file = common.MakeTempFile(prefix="payload-properties-",
1402 suffix=".txt")
1403 cmd = ["brillo_update_payload", "properties",
1404 "--payload", signed_payload_file,
1405 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001406 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1407 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001408 assert p1.returncode == 0, "brillo_update_payload properties failed"
1409
Tao Bao7c5dc572016-06-14 17:48:11 -07001410 if OPTIONS.wipe_user_data:
1411 with open(properties_file, "a") as f:
1412 f.write("POWERWASH=1\n")
1413 metadata["ota-wipe"] = "yes"
1414
Tao Baoc96316c2017-01-24 22:10:49 -08001415 # Add the signed payload file and properties into the zip. In order to
1416 # support streaming, we pack payload.bin, payload_properties.txt and
1417 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1418 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001419 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1420 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001421 common.ZipWrite(output_zip, properties_file,
1422 arcname="payload_properties.txt",
1423 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001424
Tianjie Xucfa86222016-03-07 16:31:19 -08001425 # If dm-verity is supported for the device, copy contents of care_map
1426 # into A/B OTA package.
1427 if OPTIONS.info_dict.get("verity") == "true":
1428 target_zip = zipfile.ZipFile(target_file, "r")
1429 care_map_path = "META/care_map.txt"
1430 namelist = target_zip.namelist()
1431 if care_map_path in namelist:
1432 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001433 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1434 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001435 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001436 print("Warning: cannot find care map file in target_file package")
Tianjie Xucfa86222016-03-07 16:31:19 -08001437 common.ZipClose(target_zip)
1438
Tao Bao2dd1c482017-02-03 16:49:39 -08001439 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001440 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001441 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001442 WriteMetadata(metadata, output_zip)
1443 common.ZipClose(output_zip)
1444
Tao Bao2dd1c482017-02-03 16:49:39 -08001445 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1446 # zip entries, as well as padding the entry headers. We do a preliminary
1447 # signing (with an incomplete metadata entry) to allow that to happen. Then
1448 # compute the zip entry offsets, write back the final metadata and do the
1449 # final signing.
1450 prelim_signing = tempfile.NamedTemporaryFile()
1451 SignOutput(temp_zip_file.name, prelim_signing.name)
1452 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001453
Tao Bao2dd1c482017-02-03 16:49:39 -08001454 # Open the signed zip. Compute the final metadata that's needed for streaming.
1455 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1456 compression=zipfile.ZIP_DEFLATED)
1457 expected_length = len(metadata['ota-streaming-property-files'])
1458 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1459 prelim_zip, reserve_space=False, expected_length=expected_length)
1460
1461 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1462 final_signing = tempfile.NamedTemporaryFile()
1463 output_zip = zipfile.ZipFile(final_signing, "w",
1464 compression=zipfile.ZIP_DEFLATED)
1465 for item in prelim_zip.infolist():
1466 if item.filename == METADATA_NAME:
1467 continue
1468
1469 data = prelim_zip.read(item.filename)
1470 out_info = copy.copy(item)
1471 common.ZipWriteStr(output_zip, out_info, data)
1472
1473 # Now write the final metadata entry.
1474 WriteMetadata(metadata, output_zip)
1475 common.ZipClose(prelim_zip)
1476 common.ZipClose(output_zip)
1477
1478 # Re-sign the package after updating the metadata entry.
1479 SignOutput(final_signing.name, output_file)
1480 final_signing.close()
1481
1482 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001483 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001484 actual = metadata['ota-streaming-property-files'].strip()
1485 expected = ComputeStreamingMetadata(output_zip)
1486 assert actual == expected, \
1487 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001488 common.ZipClose(output_zip)
1489
Tao Baoc098e9e2016-01-07 13:03:56 -08001490
Dan Albert8b72aef2015-03-23 19:13:21 -07001491class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001492 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001493 self.deferred_patch_list = None
Tao Bao89fbb0f2017-01-10 10:47:58 -08001494 print("Loading target...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001495 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001496 print("Loading source...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001497 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1498
1499 self.verbatim_targets = verbatim_targets = []
1500 self.patch_list = patch_list = []
1501 diffs = []
1502 self.renames = renames = {}
1503 known_paths = set()
1504 largest_source_size = 0
1505
1506 matching_file_cache = {}
1507 for fn, sf in source_data.items():
1508 assert fn == sf.name
1509 matching_file_cache["path:" + fn] = sf
1510 if fn in target_data.keys():
1511 AddToKnownPaths(fn, known_paths)
1512 # Only allow eligibility for filename/sha matching
1513 # if there isn't a perfect path match.
1514 if target_data.get(sf.name) is None:
1515 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1516 matching_file_cache["sha:" + sf.sha1] = sf
1517
1518 for fn in sorted(target_data.keys()):
1519 tf = target_data[fn]
1520 assert fn == tf.name
1521 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1522 if sf is not None and sf.name != tf.name:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001523 print("File has moved from " + sf.name + " to " + tf.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001524 renames[sf.name] = tf
1525
1526 if sf is None or fn in OPTIONS.require_verbatim:
1527 # This file should be included verbatim
1528 if fn in OPTIONS.prohibit_verbatim:
1529 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Tao Bao89fbb0f2017-01-10 10:47:58 -08001530 print("send", fn, "verbatim")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001531 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001532 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001533 if fn in target_data.keys():
1534 AddToKnownPaths(fn, known_paths)
1535 elif tf.sha1 != sf.sha1:
1536 # File is different; consider sending as a patch
1537 diffs.append(common.Difference(tf, sf))
1538 else:
1539 # Target file data identical to source (may still be renamed)
1540 pass
1541
1542 common.ComputeDifferences(diffs)
1543
1544 for diff in diffs:
1545 tf, sf, d = diff.GetPatch()
1546 path = "/".join(tf.name.split("/")[:-1])
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001547 if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001548 path not in known_paths:
1549 # patch is almost as big as the file; don't bother patching
1550 # or a patch + rename cannot take place due to the target
1551 # directory not existing
1552 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001553 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001554 if sf.name in renames:
1555 del renames[sf.name]
1556 AddToKnownPaths(tf.name, known_paths)
1557 else:
1558 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1559 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1560 largest_source_size = max(largest_source_size, sf.size)
1561
1562 self.largest_source_size = largest_source_size
1563
1564 def EmitVerification(self, script):
1565 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001566 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001567 if tf.name != sf.name:
1568 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1569 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1570 so_far += sf.size
1571 return so_far
1572
Michael Runge63f01de2014-10-28 19:24:19 -07001573 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001574 for fn, _, sha1 in self.verbatim_targets:
1575 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001576 script.FileCheck("/"+fn, sha1)
1577 for tf, _, _, _ in self.patch_list:
1578 script.FileCheck(tf.name, tf.sha1)
1579
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001580 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001581 file_list = ["/" + i[0] for i in self.verbatim_targets]
1582 file_list += ["/" + i for i in self.source_data
1583 if i not in self.target_data and i not in self.renames]
1584 file_list += list(extras)
1585 # Sort the list in descending order, which removes all the files first
1586 # before attempting to remove the folder. (Bug: 22960996)
1587 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001588
1589 def TotalPatchSize(self):
1590 return sum(i[1].size for i in self.patch_list)
1591
1592 def EmitPatches(self, script, total_patch_size, so_far):
1593 self.deferred_patch_list = deferred_patch_list = []
1594 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001595 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001596 if tf.name == "system/build.prop":
1597 deferred_patch_list.append(item)
1598 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001599 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001600 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001601 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1602 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001603 so_far += tf.size
1604 script.SetProgress(so_far / total_patch_size)
1605 return so_far
1606
1607 def EmitDeferredPatches(self, script):
1608 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001609 tf, sf, _, _ = item
1610 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1611 "patch/" + sf.name + ".p")
1612 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001613
1614 def EmitRenames(self, script):
1615 if len(self.renames) > 0:
1616 script.Print("Renaming files...")
1617 for src, tgt in self.renames.iteritems():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001618 print("Renaming " + src + " to " + tgt.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001619 script.RenameFile(src, tgt.name)
1620
1621
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001622def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001623 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1624 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1625
Doug Zongker26e66192014-02-20 13:22:07 -08001626 if (OPTIONS.block_based and
1627 target_has_recovery_patch and
1628 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001629 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1630
Doug Zongker37974732010-09-16 17:44:38 -07001631 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1632 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001633
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001634 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001635 print("WARNING: generating edify script for a source that "
1636 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001637 script = edify_generator.EdifyGenerator(
1638 source_version, OPTIONS.target_info_dict,
1639 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001640
Tao Bao34b47bf2015-06-22 19:17:41 -07001641 recovery_mount_options = OPTIONS.source_info_dict.get(
1642 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001643 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1644 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -07001645 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001646 if source_oem_props or target_oem_props:
Michael Runge6e836112014-04-15 17:40:21 -07001647 if OPTIONS.oem_source is None:
1648 raise common.ExternalError("OEM source required for this build")
Tao Bao1bb5a182016-03-04 09:45:03 -08001649 if not OPTIONS.oem_no_mount:
1650 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001651 oem_dict = common.LoadDictionaryFromLines(
1652 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001653
Dan Albert8b72aef2015-03-23 19:13:21 -07001654 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001655 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
1656 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001657 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001658 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001659
Tao Bao5d182562016-02-23 11:38:39 -08001660 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
1661 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1662 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1663
1664 if OPTIONS.downgrade:
1665 metadata["ota-downgrade"] = "yes"
1666 if not is_downgrade:
1667 raise RuntimeError("--downgrade specified but no downgrade detected: "
1668 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
1669 else:
1670 if is_downgrade:
1671 # Non-fatal here to allow generating such a package which may require
1672 # manual work to adjust the post-timestamp. A legit use case is that we
1673 # cut a new build C (after having A and B), but want to enfore the
1674 # update path of A -> C -> B. Specifying --downgrade may not help since
1675 # that would enforce a data wipe for C -> B update.
1676 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
1677 "The package may not be deployed properly. "
1678 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
1679 metadata["post-timestamp"] = post_timestamp
1680
Doug Zongker05d3dea2009-06-22 11:32:31 -07001681 device_specific = common.DeviceSpecificParams(
1682 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001683 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001684 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001685 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001686 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001687 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001688 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001689 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001690
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001691 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001692 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001693 if HasVendorPartition(target_zip):
1694 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001695 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001696 else:
1697 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001698
Tao Bao3e30d972016-03-15 13:20:19 -07001699 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001700 OPTIONS.target_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -07001701 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001702 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001703
Tao Bao3e30d972016-03-15 13:20:19 -07001704 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001705 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001706 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001707 script.AssertSomeThumbprint(
1708 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1709 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001710 elif source_oem_props is None and target_oem_props is not None:
1711 script.AssertFingerprintOrThumbprint(
1712 source_fp,
1713 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1714 else:
1715 script.AssertFingerprintOrThumbprint(
1716 target_fp,
1717 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001718
Doug Zongker2ea21062010-04-28 16:05:21 -07001719 metadata["pre-build"] = source_fp
1720 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001721 metadata["pre-build-incremental"] = GetBuildProp(
1722 "ro.build.version.incremental", OPTIONS.source_info_dict)
1723 metadata["post-build-incremental"] = GetBuildProp(
1724 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001725
Doug Zongker55d93282011-01-25 17:03:34 -08001726 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001727 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1728 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001729 target_boot = common.GetBootableImage(
1730 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001731 updating_boot = (not OPTIONS.two_step and
1732 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001733
Doug Zongker55d93282011-01-25 17:03:34 -08001734 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001735 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1736 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001737 target_recovery = common.GetBootableImage(
1738 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001739 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001740
Doug Zongker881dd402009-09-20 14:03:55 -07001741 # Here's how we divide up the progress bar:
1742 # 0.1 for verifying the start state (PatchCheck calls)
1743 # 0.8 for applying patches (ApplyPatch calls)
1744 # 0.1 for unpacking verbatim files, symlinking, and doing the
1745 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001746
Michael Runge6e836112014-04-15 17:40:21 -07001747 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001748 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001749
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001750 # Two-step incremental package strategy (in chronological order,
1751 # which is *not* the order in which the generated script has
1752 # things):
1753 #
1754 # if stage is not "2/3" or "3/3":
1755 # do verification on current system
1756 # write recovery image to boot partition
1757 # set stage to "2/3"
1758 # reboot to boot partition and restart recovery
1759 # else if stage is "2/3":
1760 # write recovery image to recovery partition
1761 # set stage to "3/3"
1762 # reboot to recovery partition and restart recovery
1763 # else:
1764 # (stage must be "3/3")
1765 # perform update:
1766 # patch system files, etc.
1767 # force full install of new boot image
1768 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001769 # complete script normally
1770 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001771
1772 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001773 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001774 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001775 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001776 assert fs.fs_type.upper() == "EMMC", \
1777 "two-step packages only supported on devices with EMMC /misc partitions"
1778 bcb_dev = {"bcb_dev": fs.device}
1779 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1780 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001781if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001782""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001783
1784 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1785 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001786 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001787 script.WriteRawImage("/recovery", "recovery.img")
1788 script.AppendExtra("""
1789set_stage("%(bcb_dev)s", "3/3");
1790reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001791else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001792""" % bcb_dev)
1793
Tao Baod42e97e2016-11-30 12:11:57 -08001794 # Stage 1/3: (a) Verify the current system.
1795 script.Comment("Stage 1/3")
1796
Tao Bao6c55a8a2015-04-08 15:30:27 -07001797 # Dump fingerprints
1798 script.Print("Source: %s" % (source_fp,))
1799 script.Print("Target: %s" % (target_fp,))
1800
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001801 script.Print("Verifying current system...")
1802
Doug Zongkere5ff5902012-01-17 10:55:37 -08001803 device_specific.IncrementalOTA_VerifyBegin()
1804
Doug Zongker881dd402009-09-20 14:03:55 -07001805 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001806 so_far = system_diff.EmitVerification(script)
1807 if vendor_diff:
1808 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001809
Tao Baod8d14be2016-02-04 14:26:02 -08001810 size = []
1811 if system_diff.patch_list:
1812 size.append(system_diff.largest_source_size)
1813 if vendor_diff:
1814 if vendor_diff.patch_list:
1815 size.append(vendor_diff.largest_source_size)
1816
Doug Zongker5da317e2009-06-02 13:38:17 -07001817 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001818 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001819 _, _, d = d.ComputePatch()
Tao Bao89fbb0f2017-01-10 10:47:58 -08001820 print("boot target: %d source: %d diff: %d" % (
1821 target_boot.size, source_boot.size, len(d)))
Doug Zongker5da317e2009-06-02 13:38:17 -07001822
Doug Zongker048e7ca2009-06-15 14:31:53 -07001823 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001824
Tao Baodd24da92015-07-29 14:09:23 -07001825 boot_type, boot_device = common.GetTypeAndDevice(
1826 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001827
1828 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1829 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001830 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001831 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001832 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001833 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001834
Tao Baod8d14be2016-02-04 14:26:02 -08001835 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001836 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001837
Doug Zongker05d3dea2009-06-22 11:32:31 -07001838 device_specific.IncrementalOTA_VerifyEnd()
1839
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001840 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001841 # Stage 1/3: (b) Write recovery image to /boot.
1842 _WriteRecoveryImageToBoot(script, output_zip)
1843
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001844 script.AppendExtra("""
1845set_stage("%(bcb_dev)s", "2/3");
1846reboot_now("%(bcb_dev)s", "");
1847else
1848""" % bcb_dev)
1849
Tao Baod42e97e2016-11-30 12:11:57 -08001850 # Stage 3/3: Make changes.
1851 script.Comment("Stage 3/3")
1852
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001853 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001854
Doug Zongkere5ff5902012-01-17 10:55:37 -08001855 device_specific.IncrementalOTA_InstallBegin()
1856
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001857 if OPTIONS.two_step:
1858 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1859 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001860 print("writing full boot image (forced by two-step mode)")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001861
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001862 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001863 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1864 if vendor_diff:
1865 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001866
Doug Zongker881dd402009-09-20 14:03:55 -07001867 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001868 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1869 if vendor_diff:
1870 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001871 if updating_boot:
1872 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001873
1874 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001875 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1876 if vendor_diff:
1877 script.Print("Patching vendor files...")
1878 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001879
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001880 if not OPTIONS.two_step:
1881 if updating_boot:
1882 # Produce the boot image by applying a patch to the current
1883 # contents of the boot partition, and write it back to the
1884 # partition.
1885 script.Print("Patching boot image...")
1886 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1887 % (boot_type, boot_device,
1888 source_boot.size, source_boot.sha1,
1889 target_boot.size, target_boot.sha1),
1890 "-",
1891 target_boot.size, target_boot.sha1,
1892 source_boot.sha1, "patch/boot.img.p")
1893 so_far += target_boot.size
1894 script.SetProgress(so_far / total_patch_size)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001895 print("boot image changed; including.")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001896 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001897 print("boot image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001898
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001899 system_items = ItemSet("system", "META/filesystem_config.txt")
1900 if vendor_diff:
1901 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1902
Doug Zongkereef39442009-04-02 12:14:19 -07001903 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001904 # Recovery is generated as a patch using both the boot image
1905 # (which contains the same linux kernel as recovery) and the file
1906 # /system/etc/recovery-resource.dat (which contains all the images
1907 # used in the recovery UI) as sources. This lets us minimize the
1908 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001909 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001910 # For older builds where recovery-resource.dat is not present, we
1911 # use only the boot image as the source.
1912
Doug Zongkerc9253822014-02-04 12:17:58 -08001913 if not target_has_recovery_patch:
1914 def output_sink(fn, data):
1915 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001916 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001917
1918 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1919 target_recovery, target_boot)
1920 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001921 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001922 "/system/etc/install-recovery.sh"])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001923 print("recovery image changed; including as patch from boot.")
Doug Zongkereef39442009-04-02 12:14:19 -07001924 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001925 print("recovery image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001926
Doug Zongker881dd402009-09-20 14:03:55 -07001927 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001928
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001929 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1930 if vendor_diff:
1931 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1932
1933 temp_script = script.MakeTemporary()
1934 system_items.GetMetadata(target_zip)
1935 system_items.Get("system").SetPermissions(temp_script)
1936 if vendor_diff:
1937 vendor_items.GetMetadata(target_zip)
1938 vendor_items.Get("vendor").SetPermissions(temp_script)
1939
1940 # Note that this call will mess up the trees of Items, so make sure
1941 # we're done with them.
1942 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1943 if vendor_diff:
1944 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001945
1946 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001947 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1948
1949 # Delete all the symlinks in source that aren't in target. This
1950 # needs to happen before verbatim files are unpacked, in case a
1951 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001952
1953 # If a symlink in the source will be replaced by a regular file, we cannot
1954 # delete the symlink/file in case the package gets applied again. For such
1955 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1956 # (Bug: 23646151)
1957 replaced_symlinks = dict()
1958 if system_diff:
1959 for i in system_diff.verbatim_targets:
1960 replaced_symlinks["/%s" % (i[0],)] = i[2]
1961 if vendor_diff:
1962 for i in vendor_diff.verbatim_targets:
1963 replaced_symlinks["/%s" % (i[0],)] = i[2]
1964
1965 if system_diff:
1966 for tf in system_diff.renames.values():
1967 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1968 if vendor_diff:
1969 for tf in vendor_diff.renames.values():
1970 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1971
1972 always_delete = []
1973 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001974 for dest, link in source_symlinks:
1975 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001976 if link in replaced_symlinks:
1977 may_delete.append((link, replaced_symlinks[link]))
1978 else:
1979 always_delete.append(link)
1980 script.DeleteFiles(always_delete)
1981 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001982
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001983 if system_diff.verbatim_targets:
1984 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001985 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001986 if vendor_diff and vendor_diff.verbatim_targets:
1987 script.Print("Unpacking new vendor files...")
1988 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001989
Doug Zongkerc9253822014-02-04 12:17:58 -08001990 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001991 script.Print("Unpacking new recovery...")
1992 script.UnpackPackageDir("recovery", "/system")
1993
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001994 system_diff.EmitRenames(script)
1995 if vendor_diff:
1996 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001997
Doug Zongker05d3dea2009-06-22 11:32:31 -07001998 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001999
2000 # Create all the symlinks that don't already exist, or point to
2001 # somewhere different than what we want. Delete each symlink before
2002 # creating it, since the 'symlink' command won't overwrite.
2003 to_create = []
2004 for dest, link in target_symlinks:
2005 if link in source_symlinks_d:
2006 if dest != source_symlinks_d[link]:
2007 to_create.append((dest, link))
2008 else:
2009 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002010 script.DeleteFiles([i[1] for i in to_create])
2011 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07002012
2013 # Now that the symlinks are created, we can set all the
2014 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002015 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07002016
Doug Zongker881dd402009-09-20 14:03:55 -07002017 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07002018 device_specific.IncrementalOTA_InstallEnd()
2019
Doug Zongker1c390a22009-05-14 19:06:36 -07002020 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07002021 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07002022
Doug Zongkere92f15a2011-08-26 13:46:40 -07002023 # Patch the build.prop file last, so if something fails but the
2024 # device can still come up, it appears to be the old build and will
2025 # get set the OTA package again to retry.
2026 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07002027 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07002028
Doug Zongker922206e2014-03-04 13:16:24 -08002029 if OPTIONS.wipe_user_data:
2030 script.Print("Erasing user data...")
2031 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08002032 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08002033
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002034 if OPTIONS.two_step:
2035 script.AppendExtra("""
2036set_stage("%(bcb_dev)s", "");
2037endif;
2038endif;
2039""" % bcb_dev)
2040
Michael Runge63f01de2014-10-28 19:24:19 -07002041 if OPTIONS.verify and system_diff:
2042 script.Print("Remounting and verifying system partition files...")
2043 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08002044 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002045 system_diff.EmitExplicitTargetVerification(script)
2046
2047 if OPTIONS.verify and vendor_diff:
2048 script.Print("Remounting and verifying vendor partition files...")
2049 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08002050 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002051 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08002052
2053 # For downgrade OTAs, we prefer to use the update-binary in the source
2054 # build that is actually newer than the one in the target build.
2055 if OPTIONS.downgrade:
2056 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
2057 else:
2058 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07002059
Tao Baod8d14be2016-02-04 14:26:02 -08002060 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07002061 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07002062
2063
2064def main(argv):
2065
2066 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08002067 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002068 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07002069 elif o in ("-k", "--package_key"):
2070 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002071 elif o in ("-i", "--incremental_from"):
2072 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002073 elif o == "--full_radio":
2074 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002075 elif o == "--full_bootloader":
2076 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002077 elif o in ("-w", "--wipe_user_data"):
2078 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002079 elif o == "--downgrade":
2080 OPTIONS.downgrade = True
2081 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07002082 elif o in ("-o", "--oem_settings"):
2083 OPTIONS.oem_source = a
Tao Bao8608cde2016-02-25 19:49:55 -08002084 elif o == "--oem_no_mount":
2085 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002086 elif o in ("-e", "--extra_script"):
2087 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002088 elif o in ("-t", "--worker_threads"):
2089 if a.isdigit():
2090 OPTIONS.worker_threads = int(a)
2091 else:
2092 raise ValueError("Cannot parse value %r for option %r - only "
2093 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002094 elif o in ("-2", "--two_step"):
2095 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002096 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002097 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002098 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002099 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002100 elif o == "--block":
2101 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002102 elif o in ("-b", "--binary"):
2103 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002104 elif o in ("--no_fallback_to_full",):
2105 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002106 elif o == "--stash_threshold":
2107 try:
2108 OPTIONS.stash_threshold = float(a)
2109 except ValueError:
2110 raise ValueError("Cannot parse value %r for option %r - expecting "
2111 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002112 elif o == "--gen_verify":
2113 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002114 elif o == "--log_diff":
2115 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002116 elif o == "--payload_signer":
2117 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002118 elif o == "--payload_signer_args":
2119 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002120 else:
2121 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002122 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002123
2124 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08002125 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002126 extra_long_opts=[
2127 "board_config=",
2128 "package_key=",
2129 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002130 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002131 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002132 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002133 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07002134 "extra_script=",
2135 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002136 "two_step",
2137 "no_signing",
2138 "block",
2139 "binary=",
2140 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002141 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002142 "verify",
2143 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002144 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002145 "gen_verify",
2146 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002147 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002148 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002149 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002150
2151 if len(args) != 2:
2152 common.Usage(__doc__)
2153 sys.exit(1)
2154
Tao Bao5d182562016-02-23 11:38:39 -08002155 if OPTIONS.downgrade:
2156 # Sanity check to enforce a data wipe.
2157 if not OPTIONS.wipe_user_data:
2158 raise ValueError("Cannot downgrade without a data wipe")
2159
2160 # We should only allow downgrading incrementals (as opposed to full).
2161 # Otherwise the device may go back from arbitrary build with this full
2162 # OTA package.
2163 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002164 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002165
Tao Baoc098e9e2016-01-07 13:03:56 -08002166 # Load the dict file from the zip directly to have a peek at the OTA type.
2167 # For packages using A/B update, unzipping is not needed.
2168 input_zip = zipfile.ZipFile(args[0], "r")
2169 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2170 common.ZipClose(input_zip)
2171
2172 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2173
2174 if ab_update:
2175 if OPTIONS.incremental_source is not None:
2176 OPTIONS.target_info_dict = OPTIONS.info_dict
2177 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2178 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2179 common.ZipClose(source_zip)
2180
2181 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002182 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002183 common.DumpInfoDict(OPTIONS.info_dict)
2184
2185 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002186 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002187 common.DumpInfoDict(OPTIONS.source_info_dict)
2188
2189 WriteABOTAPackageWithBrilloScript(
2190 target_file=args[0],
2191 output_file=args[1],
2192 source_file=OPTIONS.incremental_source)
2193
Tao Bao89fbb0f2017-01-10 10:47:58 -08002194 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002195 return
2196
Doug Zongker1c390a22009-05-14 19:06:36 -07002197 if OPTIONS.extra_script is not None:
2198 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2199
Tao Bao89fbb0f2017-01-10 10:47:58 -08002200 print("unzipping target target-files...")
Doug Zongker55d93282011-01-25 17:03:34 -08002201 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002202
Doug Zongkereef39442009-04-02 12:14:19 -07002203 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002204 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002205
Doug Zongker37974732010-09-16 17:44:38 -07002206 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002207 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07002208 common.DumpInfoDict(OPTIONS.info_dict)
2209
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002210 # If the caller explicitly specified the device-specific extensions
2211 # path via -s/--device_specific, use that. Otherwise, use
2212 # META/releasetools.py if it is present in the target target_files.
2213 # Otherwise, take the path of the file from 'tool_extensions' in the
2214 # info dict and look for that in the local filesystem, relative to
2215 # the current directory.
2216
Doug Zongker37974732010-09-16 17:44:38 -07002217 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002218 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2219 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08002220 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002221 OPTIONS.device_specific = from_input
2222 else:
2223 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2224
Doug Zongker37974732010-09-16 17:44:38 -07002225 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002226 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002227
Tao Baoc098e9e2016-01-07 13:03:56 -08002228 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002229 raise common.ExternalError(
2230 "--- target build has specified no recovery ---")
2231
Tao Bao767e3ac2015-11-10 12:19:19 -08002232 # Use the default key to sign the package if not specified with package_key.
2233 if not OPTIONS.no_signing:
2234 if OPTIONS.package_key is None:
2235 OPTIONS.package_key = OPTIONS.info_dict.get(
2236 "default_system_dev_certificate",
2237 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002238
Tao Bao767e3ac2015-11-10 12:19:19 -08002239 # Set up the output zip. Create a temporary zip file if signing is needed.
2240 if OPTIONS.no_signing:
2241 if os.path.exists(args[1]):
2242 os.unlink(args[1])
2243 output_zip = zipfile.ZipFile(args[1], "w",
2244 compression=zipfile.ZIP_DEFLATED)
2245 else:
2246 temp_zip_file = tempfile.NamedTemporaryFile()
2247 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2248 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002249
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002250 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002251 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002252 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002253 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002254 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002255
Tao Bao9bc6bb22015-11-09 16:58:28 -08002256 # Generate a verify package.
2257 if OPTIONS.gen_verify:
2258 WriteVerifyPackage(input_zip, output_zip)
2259
Tao Bao767e3ac2015-11-10 12:19:19 -08002260 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002261 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002262 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002263
2264 # Generate an incremental OTA. It will fall back to generate a full OTA on
2265 # failure unless no_fallback_to_full is specified.
2266 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002267 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08002268 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2269 OPTIONS.incremental_source)
2270 OPTIONS.target_info_dict = OPTIONS.info_dict
2271 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2272 OPTIONS.source_tmp)
2273 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002274 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002275 common.DumpInfoDict(OPTIONS.source_info_dict)
2276 try:
2277 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002278 if OPTIONS.log_diff:
2279 out_file = open(OPTIONS.log_diff, 'w')
2280 import target_files_diff
2281 target_files_diff.recursiveDiff('',
2282 OPTIONS.source_tmp,
2283 OPTIONS.input_tmp,
2284 out_file)
2285 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002286 except ValueError:
2287 if not OPTIONS.fallback_to_full:
2288 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08002289 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002290 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002291 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002292
Tao Bao767e3ac2015-11-10 12:19:19 -08002293 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002294
Tao Bao767e3ac2015-11-10 12:19:19 -08002295 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002296 if not OPTIONS.no_signing:
2297 SignOutput(temp_zip_file.name, args[1])
2298 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002299
Tao Bao89fbb0f2017-01-10 10:47:58 -08002300 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002301
2302
2303if __name__ == '__main__':
2304 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002305 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002306 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002307 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002308 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002309 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002310 finally:
2311 common.Cleanup()