blob: f6b60ad84f2cc78156242893076f96e19a78cddc [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
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080053 -o (--oem_settings) <main_file[,additional_files...]>
54 Comma seperated list of files used to specify the expected OEM-specific
55 properties on the OEM partition of the intended device.
56 Multiple expected values can be used by providing multiple files.
57
Michael Runge6e836112014-04-15 17:40:21 -070058
Tao Bao8608cde2016-02-25 19:49:55 -080059 --oem_no_mount
60 For devices with OEM-specific properties but without an OEM partition,
61 do not mount the OEM partition in the updater-script. This should be
62 very rarely used, since it's expected to have a dedicated OEM partition
63 for OEM-specific properties. Only meaningful when -o is specified.
64
Doug Zongkerdbfaae52009-04-21 17:12:54 -070065 -w (--wipe_user_data)
66 Generate an OTA package that will wipe the user data partition
67 when installed.
68
Tao Bao5d182562016-02-23 11:38:39 -080069 --downgrade
70 Intentionally generate an incremental OTA that updates from a newer
71 build to an older one (based on timestamp comparison). "post-timestamp"
72 will be replaced by "ota-downgrade=yes" in the metadata file. A data
73 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080074 the metadata file. The update-binary in the source build will be used in
Tao Bao3e6161a2017-02-28 11:48:48 -080075 the OTA package, unless --binary flag is specified. Please also check the
76 doc for --override_timestamp below.
77
78 --override_timestamp
79 Intentionally generate an incremental OTA that updates from a newer
80 build to an older one (based on timestamp comparison), by overriding the
81 timestamp in package metadata. This differs from --downgrade flag: we
82 know for sure this is NOT an actual downgrade case, but two builds are
83 cut in a reverse order. A legit use case is that we cut a new build C
84 (after having A and B), but want to enfore an update path of A -> C -> B.
85 Specifying --downgrade may not help since that would enforce a data wipe
86 for C -> B update. The value of "post-timestamp" will be set to the newer
87 timestamp plus one, so that the package can be pushed and applied.
Tao Bao5d182562016-02-23 11:38:39 -080088
Doug Zongker1c390a22009-05-14 19:06:36 -070089 -e (--extra_script) <file>
90 Insert the contents of file at the end of the update script.
91
Doug Zongker9b23f2c2013-11-25 14:44:12 -080092 -2 (--two_step)
93 Generate a 'two-step' OTA package, where recovery is updated
94 first, so that any changes made to the system partition are done
95 using the new recovery (new kernel, etc.).
96
Doug Zongker26e66192014-02-20 13:22:07 -080097 --block
98 Generate a block-based OTA if possible. Will fall back to a
99 file-based OTA if the target_files is older and doesn't support
100 block-based OTAs.
101
Doug Zongker25568482014-03-03 10:21:27 -0800102 -b (--binary) <file>
103 Use the given binary as the update-binary in the output package,
104 instead of the binary in the build's target_files. Use for
105 development only.
106
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200107 -t (--worker_threads) <int>
108 Specifies the number of worker-threads that will be used when
109 generating patches for incremental updates (defaults to 3).
110
Tao Bao8dcf7382015-05-21 14:09:49 -0700111 --stash_threshold <float>
112 Specifies the threshold that will be used to compute the maximum
113 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800114
115 --gen_verify
116 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800117
118 --log_diff <file>
119 Generate a log file that shows the differences in the source and target
120 builds for an incremental package. This option is only meaningful when
121 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700122
123 --payload_signer <signer>
124 Specify the signer when signing the payload and metadata for A/B OTAs.
125 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
126 with the package private key. If the private key cannot be accessed
127 directly, a payload signer that knows how to do that should be specified.
128 The signer will be supplied with "-inkey <path_to_key>",
129 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700130
131 --payload_signer_args <args>
132 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700133"""
134
Tao Bao89fbb0f2017-01-10 10:47:58 -0800135from __future__ import print_function
136
Doug Zongkereef39442009-04-02 12:14:19 -0700137import sys
138
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800139if sys.hexversion < 0x02070000:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800140 print("Python 2.7 or newer is required.", file=sys.stderr)
Doug Zongkereef39442009-04-02 12:14:19 -0700141 sys.exit(1)
142
Tao Bao2dd1c482017-02-03 16:49:39 -0800143import copy
Doug Zongkerfc44a512014-08-26 13:10:25 -0700144import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800145import os.path
Tao Baoc098e9e2016-01-07 13:03:56 -0800146import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700147import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700148import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700149import zipfile
150
151import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700152import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700153import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700154
155OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700156OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700157OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700158OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700159OPTIONS.require_verbatim = set()
160OPTIONS.prohibit_verbatim = set(("system/build.prop",))
161OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700162OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800163OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800164OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700165OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700166OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
167if OPTIONS.worker_threads == 0:
168 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800169OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900170OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800171OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800172OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700173OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800174OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700175OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700176OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700177OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700178# Stash size cannot exceed cache_size * threshold.
179OPTIONS.cache_size = None
180OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800181OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800182OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700183OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700184OPTIONS.payload_signer_args = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700185
Tao Bao2dd1c482017-02-03 16:49:39 -0800186METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800187UNZIP_PATTERN = ['IMAGES/*', 'META/*']
188
Tao Bao2dd1c482017-02-03 16:49:39 -0800189
Doug Zongkereef39442009-04-02 12:14:19 -0700190def MostPopularKey(d, default):
191 """Given a dict, return the key corresponding to the largest
192 value. Returns 'default' if the dict is empty."""
193 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700194 if not x:
195 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700196 x.sort()
197 return x[-1][1]
198
199
200def IsSymlink(info):
201 """Return true if the zipfile.ZipInfo object passed in represents a
202 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700203 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700204
Hristo Bojinov96be7202010-08-02 10:26:17 -0700205def IsRegular(info):
206 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700207 regular file."""
208 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700209
Michael Runge4038aa82013-12-13 18:06:28 -0800210def ClosestFileMatch(src, tgtfiles, existing):
211 """Returns the closest file match between a source file and list
212 of potential matches. The exact filename match is preferred,
213 then the sha1 is searched for, and finally a file with the same
214 basename is evaluated. Rename support in the updater-binary is
215 required for the latter checks to be used."""
216
217 result = tgtfiles.get("path:" + src.name)
218 if result is not None:
219 return result
220
221 if not OPTIONS.target_info_dict.get("update_rename_support", False):
222 return None
223
224 if src.size < 1000:
225 return None
226
227 result = tgtfiles.get("sha1:" + src.sha1)
228 if result is not None and existing.get(result.name) is None:
229 return result
230 result = tgtfiles.get("file:" + src.name.split("/")[-1])
231 if result is not None and existing.get(result.name) is None:
232 return result
233 return None
234
Dan Albert8b72aef2015-03-23 19:13:21 -0700235class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700236 def __init__(self, partition, fs_config):
237 self.partition = partition
238 self.fs_config = fs_config
239 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700240
Dan Albert8b72aef2015-03-23 19:13:21 -0700241 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700242 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700243 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700244 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700245
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700246 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700247 # The target_files contains a record of what the uid,
248 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700249 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700250
251 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700252 if not line:
253 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700254 columns = line.split()
255 name, uid, gid, mode = columns[:4]
256 selabel = None
257 capabilities = None
258
259 # After the first 4 columns, there are a series of key=value
260 # pairs. Extract out the fields we care about.
261 for element in columns[4:]:
262 key, value = element.split("=")
263 if key == "selabel":
264 selabel = value
265 if key == "capabilities":
266 capabilities = value
267
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700268 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700269 if i is not None:
270 i.uid = int(uid)
271 i.gid = int(gid)
272 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700273 i.selabel = selabel
274 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700275 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700276 i.children.sort(key=lambda i: i.name)
277
Tao Baof2cffbd2015-07-22 12:33:18 -0700278 # Set metadata for the files generated by this script. For full recovery
279 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700280 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700281 if i:
282 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700283 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700284 if i:
285 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700286
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700287
Dan Albert8b72aef2015-03-23 19:13:21 -0700288class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700289 """Items represent the metadata (user, group, mode) of files and
290 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700291 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700292 self.itemset = itemset
293 self.name = name
294 self.uid = None
295 self.gid = None
296 self.mode = None
297 self.selabel = None
298 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700299 self.is_dir = is_dir
300 self.descendants = None
301 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700302
303 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700304 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700305 self.parent.children.append(self)
306 else:
307 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700308 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700309 self.children = []
310
311 def Dump(self, indent=0):
312 if self.uid is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800313 print("%s%s %d %d %o" % (
314 " " * indent, self.name, self.uid, self.gid, self.mode))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700315 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800316 print("%s%s %s %s %s" % (
317 " " * indent, self.name, self.uid, self.gid, self.mode))
Dan Albert8b72aef2015-03-23 19:13:21 -0700318 if self.is_dir:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800319 print("%s%s" % (" " * indent, self.descendants))
320 print("%s%s" % (" " * indent, self.best_subtree))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700321 for i in self.children:
322 i.Dump(indent=indent+1)
323
Doug Zongkereef39442009-04-02 12:14:19 -0700324 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700325 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700326 all children and determine the best strategy for using set_perm_recursive
327 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700328 values. Recursively calls itself for all descendants.
329
Dan Albert8b72aef2015-03-23 19:13:21 -0700330 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
331 counting up all descendants of this node. (dmode or fmode may be None.)
332 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
333 fmode, selabel, capabilities) tuple that will match the most descendants of
334 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700335 """
336
Dan Albert8b72aef2015-03-23 19:13:21 -0700337 assert self.is_dir
338 key = (self.uid, self.gid, self.mode, None, self.selabel,
339 self.capabilities)
340 self.descendants = {key: 1}
341 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700342 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700343 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700344 for k, v in i.CountChildMetadata().iteritems():
345 d[k] = d.get(k, 0) + v
346 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700347 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700348 d[k] = d.get(k, 0) + 1
349
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700350 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
351 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700352
353 # First, find the (uid, gid) pair that matches the most
354 # descendants.
355 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700356 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700357 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
358 ug = MostPopularKey(ug, (0, 0))
359
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700360 # Now find the dmode, fmode, selabel, and capabilities that match
361 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700362 best_dmode = (0, 0o755)
363 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700364 best_selabel = (0, None)
365 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700366 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700367 if k[:2] != ug:
368 continue
369 if k[2] is not None and count >= best_dmode[0]:
370 best_dmode = (count, k[2])
371 if k[3] is not None and count >= best_fmode[0]:
372 best_fmode = (count, k[3])
373 if k[4] is not None and count >= best_selabel[0]:
374 best_selabel = (count, k[4])
375 if k[5] is not None and count >= best_capabilities[0]:
376 best_capabilities = (count, k[5])
377 self.best_subtree = ug + (
378 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700379
380 return d
381
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700382 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700383 """Append set_perm/set_perm_recursive commands to 'script' to
384 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700385 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700386
387 self.CountChildMetadata()
388
389 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700390 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
391 # that the current item (and all its children) have already been set to.
392 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700393 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700394 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700395 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700396 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700397 current = item.best_subtree
398
399 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700400 item.mode != current[2] or item.selabel != current[4] or \
401 item.capabilities != current[5]:
402 script.SetPermissions("/"+item.name, item.uid, item.gid,
403 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700404
405 for i in item.children:
406 recurse(i, current)
407 else:
408 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700409 item.mode != current[3] or item.selabel != current[4] or \
410 item.capabilities != current[5]:
411 script.SetPermissions("/"+item.name, item.uid, item.gid,
412 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700413
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700414 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700415
416
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700417def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
418 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700419 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800420 list of symlinks. output_zip may be None, in which case the copy is
421 skipped (but the other side effects still happen). substitute is an
422 optional dict of {output filename: contents} to be output instead of
423 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700424 """
425
426 symlinks = []
427
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700428 partition = itemset.partition
429
Doug Zongkereef39442009-04-02 12:14:19 -0700430 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700431 prefix = partition.upper() + "/"
432 if info.filename.startswith(prefix):
433 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700434 if IsSymlink(info):
435 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700436 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700437 else:
438 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700439 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700440 if substitute and fn in substitute and substitute[fn] is None:
441 continue
442 if output_zip is not None:
443 if substitute and fn in substitute:
444 data = substitute[fn]
445 else:
446 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700447 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700448 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700449 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700450 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700451 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700452
453 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800454 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700455
456
Doug Zongkereef39442009-04-02 12:14:19 -0700457def SignOutput(temp_zip_name, output_zip_name):
458 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
459 pw = key_passwords[OPTIONS.package_key]
460
Doug Zongker951495f2009-08-14 12:44:19 -0700461 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
462 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700463
464
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800465def AppendAssertions(script, info_dict, oem_dicts=None):
Michael Runge6e836112014-04-15 17:40:21 -0700466 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700467 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700468 device = GetBuildProp("ro.product.device", info_dict)
469 script.AssertDevice(device)
470 else:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800471 if not oem_dicts:
Dan Albert8b72aef2015-03-23 19:13:21 -0700472 raise common.ExternalError(
473 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700474 for prop in oem_props.split():
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800475 values = []
476 for oem_dict in oem_dicts:
477 if oem_dict.get(prop):
478 values.append(oem_dict[prop])
479 if not values:
Dan Albert8b72aef2015-03-23 19:13:21 -0700480 raise common.ExternalError(
481 "The OEM file is missing the property %s" % prop)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800482 script.AssertOemProperty(prop, values)
483
484
485def _LoadOemDicts(script, recovery_mount_options):
486 """Returns the list of loaded OEM properties dict."""
487 oem_dicts = None
488 if OPTIONS.oem_source is None:
489 raise common.ExternalError("OEM source required for this build")
490 if not OPTIONS.oem_no_mount:
491 script.Mount("/oem", recovery_mount_options)
492 oem_dicts = []
493 for oem_file in OPTIONS.oem_source:
494 oem_dicts.append(common.LoadDictionaryFromLines(
495 open(oem_file).readlines()))
496 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700497
Doug Zongkereef39442009-04-02 12:14:19 -0700498
Tao Baod42e97e2016-11-30 12:11:57 -0800499def _WriteRecoveryImageToBoot(script, output_zip):
500 """Find and write recovery image to /boot in two-step OTA.
501
502 In two-step OTAs, we write recovery image to /boot as the first step so that
503 we can reboot to there and install a new recovery image to /recovery.
504 A special "recovery-two-step.img" will be preferred, which encodes the correct
505 path of "/boot". Otherwise the device may show "device is corrupt" message
506 when booting into /boot.
507
508 Fall back to using the regular recovery.img if the two-step recovery image
509 doesn't exist. Note that rebuilding the special image at this point may be
510 infeasible, because we don't have the desired boot signer and keys when
511 calling ota_from_target_files.py.
512 """
513
514 recovery_two_step_img_name = "recovery-two-step.img"
515 recovery_two_step_img_path = os.path.join(
516 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
517 if os.path.exists(recovery_two_step_img_path):
518 recovery_two_step_img = common.GetBootableImage(
519 recovery_two_step_img_name, recovery_two_step_img_name,
520 OPTIONS.input_tmp, "RECOVERY")
521 common.ZipWriteStr(
522 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800523 print("two-step package: using %s in stage 1/3" % (
524 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800525 script.WriteRawImage("/boot", recovery_two_step_img_name)
526 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800527 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800528 # The "recovery.img" entry has been written into package earlier.
529 script.WriteRawImage("/boot", "recovery.img")
530
531
Doug Zongkerc9253822014-02-04 12:17:58 -0800532def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700533 namelist = [name for name in target_files_zip.namelist()]
534 return ("SYSTEM/recovery-from-boot.p" in namelist or
535 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700536
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700537def HasVendorPartition(target_files_zip):
538 try:
539 target_files_zip.getinfo("VENDOR/")
540 return True
541 except KeyError:
542 return False
543
Michael Runge6e836112014-04-15 17:40:21 -0700544def GetOemProperty(name, oem_props, oem_dict, info_dict):
545 if oem_props is not None and name in oem_props:
546 return oem_dict[name]
547 return GetBuildProp(name, info_dict)
548
549
550def CalculateFingerprint(oem_props, oem_dict, info_dict):
551 if oem_props is None:
552 return GetBuildProp("ro.build.fingerprint", info_dict)
553 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700554 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
555 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
556 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
557 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700558
Doug Zongkerfc44a512014-08-26 13:10:25 -0700559
Doug Zongker3c84f562014-07-31 11:06:30 -0700560def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700561 # Return an image object (suitable for passing to BlockImageDiff)
562 # for the 'which' partition (most be "system" or "vendor"). If a
563 # prebuilt image and file map are found in tmpdir they are used,
564 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700565
566 assert which in ("system", "vendor")
567
568 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700569 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
570 if os.path.exists(path) and os.path.exists(mappath):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800571 print("using %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700572 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700573
574 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800575 print("building %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700576
577 # This is an 'old' target-files, which does not contain images
578 # already built. Build them.
579
Doug Zongkerfc44a512014-08-26 13:10:25 -0700580 mappath = tempfile.mkstemp()[1]
581 OPTIONS.tempfiles.append(mappath)
582
Doug Zongker3c84f562014-07-31 11:06:30 -0700583 import add_img_to_target_files
584 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700585 path = add_img_to_target_files.BuildSystem(
586 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700587 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700588 path = add_img_to_target_files.BuildVendor(
589 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700590
Tao Baoff777812015-05-12 11:42:31 -0700591 # Bug: http://b/20939131
592 # In ext4 filesystems, block 0 might be changed even being mounted
593 # R/O. We add it to clobbered_blocks so that it will be written to the
594 # target unconditionally. Note that they are still part of care_map.
595 clobbered_blocks = "0"
596
597 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700598
599
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700600def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700601 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700602 # be installed on top of. For now, we expect the API just won't
603 # change very often. Similarly for fstab, it might have changed
604 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700605 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700606
Tao Bao838c68f2016-03-15 19:16:18 +0000607 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700608 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800609 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700610 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800611 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -0700612
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800613 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
614 OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700615 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800616 "pre-device": GetOemProperty("ro.product.device", oem_props,
617 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -0700618 OPTIONS.info_dict),
619 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
620 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700621
Doug Zongker05d3dea2009-06-22 11:32:31 -0700622 device_specific = common.DeviceSpecificParams(
623 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700624 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700625 output_zip=output_zip,
626 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700627 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700628 metadata=metadata,
629 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700630
Doug Zongkerc9253822014-02-04 12:17:58 -0800631 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800632 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800633
Tao Baod8d14be2016-02-04 14:26:02 -0800634 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
635
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700636 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
637 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
638 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700639
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800640 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700641 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800642
643 # Two-step package strategy (in chronological order, which is *not*
644 # the order in which the generated script has things):
645 #
646 # if stage is not "2/3" or "3/3":
647 # write recovery image to boot partition
648 # set stage to "2/3"
649 # reboot to boot partition and restart recovery
650 # else if stage is "2/3":
651 # write recovery image to recovery partition
652 # set stage to "3/3"
653 # reboot to recovery partition and restart recovery
654 # else:
655 # (stage must be "3/3")
656 # set stage to ""
657 # do normal full package installation:
658 # wipe and install system, boot image, etc.
659 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700660 # complete script normally
661 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800662
663 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
664 OPTIONS.input_tmp, "RECOVERY")
665 if OPTIONS.two_step:
666 if not OPTIONS.info_dict.get("multistage_support", None):
667 assert False, "two-step packages not supported by this build"
668 fs = OPTIONS.info_dict["fstab"]["/misc"]
669 assert fs.fs_type.upper() == "EMMC", \
670 "two-step packages only supported on devices with EMMC /misc partitions"
671 bcb_dev = {"bcb_dev": fs.device}
672 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
673 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700674if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800675""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800676
677 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
678 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800679 script.WriteRawImage("/recovery", "recovery.img")
680 script.AppendExtra("""
681set_stage("%(bcb_dev)s", "3/3");
682reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700683else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800684""" % bcb_dev)
685
Tao Baod42e97e2016-11-30 12:11:57 -0800686 # Stage 3/3: Make changes.
687 script.Comment("Stage 3/3")
688
Tao Bao6c55a8a2015-04-08 15:30:27 -0700689 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700690 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700691
Doug Zongkere5ff5902012-01-17 10:55:37 -0800692 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700693
Doug Zongker01ce19c2014-02-04 13:48:15 -0800694 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700695
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700696 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800697 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700698 if HasVendorPartition(input_zip):
699 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700700
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400701 # Place a copy of file_contexts.bin into the OTA package which will be used
702 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700703 if "selinux_fc" in OPTIONS.info_dict:
704 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500705
Michael Runge7cd99ba2014-10-22 17:21:48 -0700706 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
707
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700708 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700709 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800710
Doug Zongker26e66192014-02-20 13:22:07 -0800711 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700712 # Full OTA is done as an "incremental" against an empty source
713 # image. This has the effect of writing new data from the package
714 # to the entire partition, but lets us reuse the updater code that
715 # writes incrementals to do it.
716 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
717 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700718 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700719 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800720 else:
721 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700722 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800723 if not has_recovery_patch:
724 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800725 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700726
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700727 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800728 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700729
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700730 boot_img = common.GetBootableImage(
731 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800732
Doug Zongker91a99c22014-05-09 13:15:01 -0700733 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800734 def output_sink(fn, data):
735 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700736 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800737
738 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
739 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700740
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700741 system_items.GetMetadata(input_zip)
742 system_items.Get("system").SetPermissions(script)
743
744 if HasVendorPartition(input_zip):
745 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
746 script.ShowProgress(0.1, 0)
747
748 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700749 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
750 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700751 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700752 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700753 else:
754 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700755 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700756 script.UnpackPackageDir("vendor", "/vendor")
757
758 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
759 script.MakeSymlinks(symlinks)
760
761 vendor_items.GetMetadata(input_zip)
762 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700763
Doug Zongker37974732010-09-16 17:44:38 -0700764 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700765 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700766
Doug Zongker01ce19c2014-02-04 13:48:15 -0800767 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700768 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700769
Doug Zongker01ce19c2014-02-04 13:48:15 -0800770 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700771 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700772
Doug Zongker1c390a22009-05-14 19:06:36 -0700773 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700774 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700775
Doug Zongker14833602010-02-02 13:12:04 -0800776 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800777
Doug Zongker922206e2014-03-04 13:16:24 -0800778 if OPTIONS.wipe_user_data:
779 script.ShowProgress(0.1, 10)
780 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700781
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800782 if OPTIONS.two_step:
783 script.AppendExtra("""
784set_stage("%(bcb_dev)s", "");
785""" % bcb_dev)
786 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800787
788 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
789 script.Comment("Stage 1/3")
790 _WriteRecoveryImageToBoot(script, output_zip)
791
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800792 script.AppendExtra("""
793set_stage("%(bcb_dev)s", "2/3");
794reboot_now("%(bcb_dev)s", "");
795endif;
796endif;
797""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800798
Tao Bao5d182562016-02-23 11:38:39 -0800799 script.SetProgress(1)
800 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800801 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700802 WriteMetadata(metadata, output_zip)
803
Doug Zongkerfc44a512014-08-26 13:10:25 -0700804
Dan Albert8e0178d2015-01-27 15:53:15 -0800805def WritePolicyConfig(file_name, output_zip):
806 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500807
Doug Zongker2ea21062010-04-28 16:05:21 -0700808
809def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800810 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
811 common.ZipWriteStr(output_zip, METADATA_NAME, value,
812 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700813
Doug Zongkerfc44a512014-08-26 13:10:25 -0700814
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700815def LoadPartitionFiles(z, partition):
816 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700817 ZipFile, and return a dict of {filename: File object}."""
818 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700819 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700820 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700821 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700822 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700823 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700824 data = z.read(info.filename)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +0900825 out[fn] = common.File(fn, data, info.compress_size)
Doug Zongker1807e702012-02-28 12:21:08 -0800826 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700827
828
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700829def GetBuildProp(prop, info_dict):
830 """Return the fingerprint of the build of a given target-files info_dict."""
831 try:
832 return info_dict.get("build.prop", {})[prop]
833 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700834 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700835
Doug Zongkerfc44a512014-08-26 13:10:25 -0700836
Michael Runge4038aa82013-12-13 18:06:28 -0800837def AddToKnownPaths(filename, known_paths):
838 if filename[-1] == "/":
839 return
840 dirs = filename.split("/")[:-1]
841 while len(dirs) > 0:
842 path = "/".join(dirs)
843 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700844 break
Michael Runge4038aa82013-12-13 18:06:28 -0800845 known_paths.add(path)
846 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700847
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700848
Tao Baob31892e2017-02-07 11:21:17 -0800849def HandleDowngradeMetadata(metadata):
850 # Only incremental OTAs are allowed to reach here.
851 assert OPTIONS.incremental_source is not None
852
853 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
854 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
855 is_downgrade = long(post_timestamp) < long(pre_timestamp)
856
857 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800858 if not is_downgrade:
859 raise RuntimeError("--downgrade specified but no downgrade detected: "
860 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800861 metadata["ota-downgrade"] = "yes"
862 elif OPTIONS.timestamp:
863 if not is_downgrade:
864 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
865 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
866 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800867 else:
868 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800869 raise RuntimeError("Downgrade detected based on timestamp check: "
870 "pre: %s, post: %s. Need to specify --timestamp OR "
871 "--downgrade to allow building the incremental." % (
872 pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800873 metadata["post-timestamp"] = post_timestamp
874
875
Geremy Condra36bd3652014-02-06 19:45:10 -0800876def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700877 # TODO(tbao): We should factor out the common parts between
878 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800879 source_version = OPTIONS.source_info_dict["recovery_api_version"]
880 target_version = OPTIONS.target_info_dict["recovery_api_version"]
881
882 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700883 print("WARNING: generating edify script for a source that "
884 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700885 script = edify_generator.EdifyGenerator(
886 source_version, OPTIONS.target_info_dict,
887 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800888
Tao Bao3806c232015-07-05 21:08:33 -0700889 recovery_mount_options = OPTIONS.source_info_dict.get(
890 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700891 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
892 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800893 oem_dicts = None
894 if source_oem_props and target_oem_props:
895 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700896
Dan Albert8b72aef2015-03-23 19:13:21 -0700897 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700898 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800899 oem_dicts and oem_dicts[0],
900 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800901 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700902 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800903
Tao Baob31892e2017-02-07 11:21:17 -0800904 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800905
Geremy Condra36bd3652014-02-06 19:45:10 -0800906 device_specific = common.DeviceSpecificParams(
907 source_zip=source_zip,
908 source_version=source_version,
909 target_zip=target_zip,
910 target_version=target_version,
911 output_zip=output_zip,
912 script=script,
913 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700914 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800915
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800916 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700917 OPTIONS.source_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800918 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700919 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800920 metadata["pre-build"] = source_fp
921 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700922 metadata["pre-build-incremental"] = GetBuildProp(
923 "ro.build.version.incremental", OPTIONS.source_info_dict)
924 metadata["post-build-incremental"] = GetBuildProp(
925 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800926
927 source_boot = common.GetBootableImage(
928 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
929 OPTIONS.source_info_dict)
930 target_boot = common.GetBootableImage(
931 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
932 updating_boot = (not OPTIONS.two_step and
933 (source_boot.data != target_boot.data))
934
Geremy Condra36bd3652014-02-06 19:45:10 -0800935 target_recovery = common.GetBootableImage(
936 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800937
Doug Zongkerfc44a512014-08-26 13:10:25 -0700938 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
939 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700940
941 blockimgdiff_version = 1
942 if OPTIONS.info_dict:
943 blockimgdiff_version = max(
944 int(i) for i in
945 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
946
Tao Baof8acad12016-07-07 09:09:58 -0700947 # Check the first block of the source system partition for remount R/W only
948 # if the filesystem is ext4.
949 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
950 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700951 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
952 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
953 # b) the blocks listed in block map may not contain all the bytes for a given
954 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700955 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
956 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
957 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700958 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800959 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700960 version=blockimgdiff_version,
961 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700962
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700963 if HasVendorPartition(target_zip):
964 if not HasVendorPartition(source_zip):
965 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700966 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
967 OPTIONS.source_info_dict)
968 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
969 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800970
971 # Check first block of vendor partition for remount R/W only if
972 # disk type is ext4
973 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800974 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700975 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700976 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800977 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700978 version=blockimgdiff_version,
979 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700980 else:
981 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800982
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800983 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800984 device_specific.IncrementalOTA_Assertions()
985
986 # Two-step incremental package strategy (in chronological order,
987 # which is *not* the order in which the generated script has
988 # things):
989 #
990 # if stage is not "2/3" or "3/3":
991 # do verification on current system
992 # write recovery image to boot partition
993 # set stage to "2/3"
994 # reboot to boot partition and restart recovery
995 # else if stage is "2/3":
996 # write recovery image to recovery partition
997 # set stage to "3/3"
998 # reboot to recovery partition and restart recovery
999 # else:
1000 # (stage must be "3/3")
1001 # perform update:
1002 # patch system files, etc.
1003 # force full install of new boot image
1004 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001005 # complete script normally
1006 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001007
1008 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001009 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -08001010 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001011 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001012 assert fs.fs_type.upper() == "EMMC", \
1013 "two-step packages only supported on devices with EMMC /misc partitions"
1014 bcb_dev = {"bcb_dev": fs.device}
1015 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1016 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001017if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001018""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001019
1020 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1021 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001022 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001023 script.WriteRawImage("/recovery", "recovery.img")
1024 script.AppendExtra("""
1025set_stage("%(bcb_dev)s", "3/3");
1026reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001027else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001028""" % bcb_dev)
1029
Tao Baod42e97e2016-11-30 12:11:57 -08001030 # Stage 1/3: (a) Verify the current system.
1031 script.Comment("Stage 1/3")
1032
Tao Bao6c55a8a2015-04-08 15:30:27 -07001033 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -08001034 script.Print("Source: %s" % (source_fp,))
1035 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001036
Geremy Condra36bd3652014-02-06 19:45:10 -08001037 script.Print("Verifying current system...")
1038
1039 device_specific.IncrementalOTA_VerifyBegin()
1040
Tao Bao3e30d972016-03-15 13:20:19 -07001041 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1042 # patching on a device that's already on the target build will damage the
1043 # system. Because operations like move don't check the block state, they
1044 # always apply the changes unconditionally.
1045 if blockimgdiff_version <= 2:
1046 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001047 script.AssertSomeFingerprint(source_fp)
1048 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001049 script.AssertSomeThumbprint(
1050 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001051
1052 else: # blockimgdiff_version > 2
1053 if source_oem_props is None and target_oem_props is None:
1054 script.AssertSomeFingerprint(source_fp, target_fp)
1055 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001056 script.AssertSomeThumbprint(
1057 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1058 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001059 elif source_oem_props is None and target_oem_props is not None:
1060 script.AssertFingerprintOrThumbprint(
1061 source_fp,
1062 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1063 else:
1064 script.AssertFingerprintOrThumbprint(
1065 target_fp,
1066 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001067
Tao Baod8d14be2016-02-04 14:26:02 -08001068 # Check the required cache size (i.e. stashed blocks).
1069 size = []
1070 if system_diff:
1071 size.append(system_diff.required_cache)
1072 if vendor_diff:
1073 size.append(vendor_diff.required_cache)
1074
Geremy Condra36bd3652014-02-06 19:45:10 -08001075 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001076 boot_type, boot_device = common.GetTypeAndDevice(
1077 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001078 d = common.Difference(target_boot, source_boot)
1079 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001080 if d is None:
1081 include_full_boot = True
1082 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1083 else:
1084 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001085
Tao Bao89fbb0f2017-01-10 10:47:58 -08001086 print("boot target: %d source: %d diff: %d" % (
1087 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001088
Doug Zongkerf8340082014-08-05 10:39:37 -07001089 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001090
Doug Zongkerf8340082014-08-05 10:39:37 -07001091 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1092 (boot_type, boot_device,
1093 source_boot.size, source_boot.sha1,
1094 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001095 size.append(target_boot.size)
1096
1097 if size:
1098 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001099
1100 device_specific.IncrementalOTA_VerifyEnd()
1101
1102 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001103 # Stage 1/3: (b) Write recovery image to /boot.
1104 _WriteRecoveryImageToBoot(script, output_zip)
1105
Geremy Condra36bd3652014-02-06 19:45:10 -08001106 script.AppendExtra("""
1107set_stage("%(bcb_dev)s", "2/3");
1108reboot_now("%(bcb_dev)s", "");
1109else
1110""" % bcb_dev)
1111
Tao Baod42e97e2016-11-30 12:11:57 -08001112 # Stage 3/3: Make changes.
1113 script.Comment("Stage 3/3")
1114
Jesse Zhao75bcea02015-01-06 10:59:53 -08001115 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001116 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001117 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001118 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001119
Geremy Condra36bd3652014-02-06 19:45:10 -08001120 script.Comment("---- start making changes here ----")
1121
1122 device_specific.IncrementalOTA_InstallBegin()
1123
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001124 system_diff.WriteScript(script, output_zip,
1125 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001126
Doug Zongkerfc44a512014-08-26 13:10:25 -07001127 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001128 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001129
1130 if OPTIONS.two_step:
1131 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1132 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001133 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001134
1135 if not OPTIONS.two_step:
1136 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001137 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001138 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001139 script.Print("Installing boot image...")
1140 script.WriteRawImage("/boot", "boot.img")
1141 else:
1142 # Produce the boot image by applying a patch to the current
1143 # contents of the boot partition, and write it back to the
1144 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001145 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001146 script.Print("Patching boot image...")
1147 script.ShowProgress(0.1, 10)
1148 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1149 % (boot_type, boot_device,
1150 source_boot.size, source_boot.sha1,
1151 target_boot.size, target_boot.sha1),
1152 "-",
1153 target_boot.size, target_boot.sha1,
1154 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001155 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001156 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001157
1158 # Do device-specific installation (eg, write radio image).
1159 device_specific.IncrementalOTA_InstallEnd()
1160
1161 if OPTIONS.extra_script is not None:
1162 script.AppendExtra(OPTIONS.extra_script)
1163
Doug Zongker922206e2014-03-04 13:16:24 -08001164 if OPTIONS.wipe_user_data:
1165 script.Print("Erasing user data...")
1166 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001167 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001168
Geremy Condra36bd3652014-02-06 19:45:10 -08001169 if OPTIONS.two_step:
1170 script.AppendExtra("""
1171set_stage("%(bcb_dev)s", "");
1172endif;
1173endif;
1174""" % bcb_dev)
1175
1176 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001177 # For downgrade OTAs, we prefer to use the update-binary in the source
1178 # build that is actually newer than the one in the target build.
1179 if OPTIONS.downgrade:
1180 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1181 else:
1182 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001183 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001184 WriteMetadata(metadata, output_zip)
1185
Doug Zongker32b527d2014-03-04 10:03:02 -08001186
Tao Bao9bc6bb22015-11-09 16:58:28 -08001187def WriteVerifyPackage(input_zip, output_zip):
1188 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1189
1190 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1191 recovery_mount_options = OPTIONS.info_dict.get(
1192 "recovery_mount_options")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001193 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -07001194 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001195 oem_dicts = _LoadOemDicts(script, oem_props, recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001196
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001197 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
1198 OPTIONS.info_dict)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001199 metadata = {
1200 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001201 "pre-device": GetOemProperty("ro.product.device", oem_props,
1202 oem_dicts and oem_dicts[0],
Tao Bao9bc6bb22015-11-09 16:58:28 -08001203 OPTIONS.info_dict),
1204 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1205 }
1206
1207 device_specific = common.DeviceSpecificParams(
1208 input_zip=input_zip,
1209 input_version=OPTIONS.info_dict["recovery_api_version"],
1210 output_zip=output_zip,
1211 script=script,
1212 input_tmp=OPTIONS.input_tmp,
1213 metadata=metadata,
1214 info_dict=OPTIONS.info_dict)
1215
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001216 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001217
1218 script.Print("Verifying device images against %s..." % target_fp)
1219 script.AppendExtra("")
1220
1221 script.Print("Verifying boot...")
1222 boot_img = common.GetBootableImage(
1223 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1224 boot_type, boot_device = common.GetTypeAndDevice(
1225 "/boot", OPTIONS.info_dict)
1226 script.Verify("%s:%s:%d:%s" % (
1227 boot_type, boot_device, boot_img.size, boot_img.sha1))
1228 script.AppendExtra("")
1229
1230 script.Print("Verifying recovery...")
1231 recovery_img = common.GetBootableImage(
1232 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1233 recovery_type, recovery_device = common.GetTypeAndDevice(
1234 "/recovery", OPTIONS.info_dict)
1235 script.Verify("%s:%s:%d:%s" % (
1236 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1237 script.AppendExtra("")
1238
1239 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1240 system_tgt.ResetFileMap()
1241 system_diff = common.BlockDifference("system", system_tgt, src=None)
1242 system_diff.WriteStrictVerifyScript(script)
1243
1244 if HasVendorPartition(input_zip):
1245 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1246 vendor_tgt.ResetFileMap()
1247 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1248 vendor_diff.WriteStrictVerifyScript(script)
1249
1250 # Device specific partitions, such as radio, bootloader and etc.
1251 device_specific.VerifyOTA_Assertions()
1252
1253 script.SetProgress(1.0)
1254 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001255 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001256 WriteMetadata(metadata, output_zip)
1257
1258
Tao Baoc098e9e2016-01-07 13:03:56 -08001259def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1260 source_file=None):
1261 """Generate an Android OTA package that has A/B update payload."""
1262
Tao Bao2dd1c482017-02-03 16:49:39 -08001263 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1264 expected_length=None):
1265 """Compute the streaming metadata for a given zip.
1266
1267 When 'reserve_space' is True, we reserve extra space for the offset and
1268 length of the metadata entry itself, although we don't know the final
1269 values until the package gets signed. This function will be called again
1270 after signing. We then write the actual values and pad the string to the
1271 length we set earlier. Note that we can't use the actual length of the
1272 metadata entry in the second run. Otherwise the offsets for other entries
1273 will be changing again.
1274 """
Tao Baoc96316c2017-01-24 22:10:49 -08001275
1276 def ComputeEntryOffsetSize(name):
1277 """Compute the zip entry offset and size."""
1278 info = zip_file.getinfo(name)
1279 offset = info.header_offset + len(info.FileHeader())
1280 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001281 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001282
1283 # payload.bin and payload_properties.txt must exist.
1284 offsets = [ComputeEntryOffsetSize('payload.bin'),
1285 ComputeEntryOffsetSize('payload_properties.txt')]
1286
1287 # care_map.txt is available only if dm-verity is enabled.
1288 if 'care_map.txt' in zip_file.namelist():
1289 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001290
1291 # 'META-INF/com/android/metadata' is required. We don't know its actual
1292 # offset and length (as well as the values for other entries). So we
1293 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1294 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1295 # beginning of the zip), as well as the possible value changes in other
1296 # entries.
1297 if reserve_space:
1298 offsets.append('metadata:' + ' ' * 10)
1299 else:
1300 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1301
1302 value = ','.join(offsets)
1303 if expected_length is not None:
1304 assert len(value) <= expected_length, \
1305 'Insufficient reserved space: reserved=%d, actual=%d' % (
1306 expected_length, len(value))
1307 value += ' ' * (expected_length - len(value))
1308 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001309
Alex Deymod8d96ec2016-06-10 16:38:31 -07001310 # The place where the output from the subprocess should go.
1311 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1312
Tao Baoc098e9e2016-01-07 13:03:56 -08001313 # Setup signing keys.
1314 if OPTIONS.package_key is None:
1315 OPTIONS.package_key = OPTIONS.info_dict.get(
1316 "default_system_dev_certificate",
1317 "build/target/product/security/testkey")
1318
Tao Baodea0f8b2016-06-20 17:55:06 -07001319 # A/B updater expects a signing key in RSA format. Gets the key ready for
1320 # later use in step 3, unless a payload_signer has been specified.
1321 if OPTIONS.payload_signer is None:
1322 cmd = ["openssl", "pkcs8",
1323 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1324 "-inform", "DER", "-nocrypt"]
1325 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1326 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001327 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1328 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001329 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001330
Tao Baodea0f8b2016-06-20 17:55:06 -07001331 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001332 temp_zip_file = tempfile.NamedTemporaryFile()
1333 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1334 compression=zipfile.ZIP_DEFLATED)
1335
1336 # Metadata to comply with Android OTA package format.
1337 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001338 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001339 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001340 oem_dicts = _LoadOemDicts(script, None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001341
1342 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001343 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001344 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001345 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1346 OPTIONS.info_dict),
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001347 "pre-device": GetOemProperty("ro.product.device", oem_props,
1348 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001349 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001350 "ota-required-cache": "0",
1351 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001352 }
1353
1354 if source_file is not None:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001355 metadata["pre-build"] = CalculateFingerprint(oem_props,
1356 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001357 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001358 metadata["pre-build-incremental"] = GetBuildProp(
1359 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001360
Tao Baob31892e2017-02-07 11:21:17 -08001361 HandleDowngradeMetadata(metadata)
1362 else:
1363 metadata["post-timestamp"] = GetBuildProp(
1364 "ro.build.date.utc", OPTIONS.info_dict)
1365
Tao Baoc098e9e2016-01-07 13:03:56 -08001366 # 1. Generate payload.
1367 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1368 cmd = ["brillo_update_payload", "generate",
1369 "--payload", payload_file,
1370 "--target_image", target_file]
1371 if source_file is not None:
1372 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001373 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1374 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001375 assert p1.returncode == 0, "brillo_update_payload generate failed"
1376
1377 # 2. Generate hashes of the payload and metadata files.
1378 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1379 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1380 cmd = ["brillo_update_payload", "hash",
1381 "--unsigned_payload", payload_file,
1382 "--signature_size", "256",
1383 "--metadata_hash_file", metadata_sig_file,
1384 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001385 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1386 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001387 assert p1.returncode == 0, "brillo_update_payload hash failed"
1388
1389 # 3. Sign the hashes and insert them back into the payload file.
1390 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1391 suffix=".bin")
1392 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1393 suffix=".bin")
1394 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001395 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001396 cmd = [OPTIONS.payload_signer]
1397 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001398 else:
1399 cmd = ["openssl", "pkeyutl", "-sign",
1400 "-inkey", rsa_key,
1401 "-pkeyopt", "digest:sha256"]
1402 cmd.extend(["-in", payload_sig_file,
1403 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001404 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1405 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001406 assert p1.returncode == 0, "openssl sign payload failed"
1407
1408 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001409 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001410 cmd = [OPTIONS.payload_signer]
1411 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001412 else:
1413 cmd = ["openssl", "pkeyutl", "-sign",
1414 "-inkey", rsa_key,
1415 "-pkeyopt", "digest:sha256"]
1416 cmd.extend(["-in", metadata_sig_file,
1417 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001418 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1419 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001420 assert p1.returncode == 0, "openssl sign metadata failed"
1421
1422 # 3c. Insert the signatures back into the payload file.
1423 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1424 suffix=".bin")
1425 cmd = ["brillo_update_payload", "sign",
1426 "--unsigned_payload", payload_file,
1427 "--payload", signed_payload_file,
1428 "--signature_size", "256",
1429 "--metadata_signature_file", signed_metadata_sig_file,
1430 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001431 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1432 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001433 assert p1.returncode == 0, "brillo_update_payload sign failed"
1434
Alex Deymo19241c12016-02-04 22:29:29 -08001435 # 4. Dump the signed payload properties.
1436 properties_file = common.MakeTempFile(prefix="payload-properties-",
1437 suffix=".txt")
1438 cmd = ["brillo_update_payload", "properties",
1439 "--payload", signed_payload_file,
1440 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001441 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1442 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001443 assert p1.returncode == 0, "brillo_update_payload properties failed"
1444
Tao Bao7c5dc572016-06-14 17:48:11 -07001445 if OPTIONS.wipe_user_data:
1446 with open(properties_file, "a") as f:
1447 f.write("POWERWASH=1\n")
1448 metadata["ota-wipe"] = "yes"
1449
Tao Baoc96316c2017-01-24 22:10:49 -08001450 # Add the signed payload file and properties into the zip. In order to
1451 # support streaming, we pack payload.bin, payload_properties.txt and
1452 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1453 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001454 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1455 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001456 common.ZipWrite(output_zip, properties_file,
1457 arcname="payload_properties.txt",
1458 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001459
Tianjie Xucfa86222016-03-07 16:31:19 -08001460 # If dm-verity is supported for the device, copy contents of care_map
1461 # into A/B OTA package.
1462 if OPTIONS.info_dict.get("verity") == "true":
1463 target_zip = zipfile.ZipFile(target_file, "r")
1464 care_map_path = "META/care_map.txt"
1465 namelist = target_zip.namelist()
1466 if care_map_path in namelist:
1467 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001468 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1469 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001470 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001471 print("Warning: cannot find care map file in target_file package")
Tianjie Xucfa86222016-03-07 16:31:19 -08001472 common.ZipClose(target_zip)
1473
Tao Bao2dd1c482017-02-03 16:49:39 -08001474 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001475 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001476 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001477 WriteMetadata(metadata, output_zip)
1478 common.ZipClose(output_zip)
1479
Tao Bao2dd1c482017-02-03 16:49:39 -08001480 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1481 # zip entries, as well as padding the entry headers. We do a preliminary
1482 # signing (with an incomplete metadata entry) to allow that to happen. Then
1483 # compute the zip entry offsets, write back the final metadata and do the
1484 # final signing.
1485 prelim_signing = tempfile.NamedTemporaryFile()
1486 SignOutput(temp_zip_file.name, prelim_signing.name)
1487 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001488
Tao Bao2dd1c482017-02-03 16:49:39 -08001489 # Open the signed zip. Compute the final metadata that's needed for streaming.
1490 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1491 compression=zipfile.ZIP_DEFLATED)
1492 expected_length = len(metadata['ota-streaming-property-files'])
1493 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1494 prelim_zip, reserve_space=False, expected_length=expected_length)
1495
1496 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1497 final_signing = tempfile.NamedTemporaryFile()
1498 output_zip = zipfile.ZipFile(final_signing, "w",
1499 compression=zipfile.ZIP_DEFLATED)
1500 for item in prelim_zip.infolist():
1501 if item.filename == METADATA_NAME:
1502 continue
1503
1504 data = prelim_zip.read(item.filename)
1505 out_info = copy.copy(item)
1506 common.ZipWriteStr(output_zip, out_info, data)
1507
1508 # Now write the final metadata entry.
1509 WriteMetadata(metadata, output_zip)
1510 common.ZipClose(prelim_zip)
1511 common.ZipClose(output_zip)
1512
1513 # Re-sign the package after updating the metadata entry.
1514 SignOutput(final_signing.name, output_file)
1515 final_signing.close()
1516
1517 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001518 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001519 actual = metadata['ota-streaming-property-files'].strip()
1520 expected = ComputeStreamingMetadata(output_zip)
1521 assert actual == expected, \
1522 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001523 common.ZipClose(output_zip)
1524
Tao Baoc098e9e2016-01-07 13:03:56 -08001525
Dan Albert8b72aef2015-03-23 19:13:21 -07001526class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001527 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001528 self.deferred_patch_list = None
Tao Bao89fbb0f2017-01-10 10:47:58 -08001529 print("Loading target...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001530 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001531 print("Loading source...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001532 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1533
1534 self.verbatim_targets = verbatim_targets = []
1535 self.patch_list = patch_list = []
1536 diffs = []
1537 self.renames = renames = {}
1538 known_paths = set()
1539 largest_source_size = 0
1540
1541 matching_file_cache = {}
1542 for fn, sf in source_data.items():
1543 assert fn == sf.name
1544 matching_file_cache["path:" + fn] = sf
1545 if fn in target_data.keys():
1546 AddToKnownPaths(fn, known_paths)
1547 # Only allow eligibility for filename/sha matching
1548 # if there isn't a perfect path match.
1549 if target_data.get(sf.name) is None:
1550 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1551 matching_file_cache["sha:" + sf.sha1] = sf
1552
1553 for fn in sorted(target_data.keys()):
1554 tf = target_data[fn]
1555 assert fn == tf.name
1556 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1557 if sf is not None and sf.name != tf.name:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001558 print("File has moved from " + sf.name + " to " + tf.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001559 renames[sf.name] = tf
1560
1561 if sf is None or fn in OPTIONS.require_verbatim:
1562 # This file should be included verbatim
1563 if fn in OPTIONS.prohibit_verbatim:
1564 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Tao Bao89fbb0f2017-01-10 10:47:58 -08001565 print("send", fn, "verbatim")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001566 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001567 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001568 if fn in target_data.keys():
1569 AddToKnownPaths(fn, known_paths)
1570 elif tf.sha1 != sf.sha1:
1571 # File is different; consider sending as a patch
1572 diffs.append(common.Difference(tf, sf))
1573 else:
1574 # Target file data identical to source (may still be renamed)
1575 pass
1576
1577 common.ComputeDifferences(diffs)
1578
1579 for diff in diffs:
1580 tf, sf, d = diff.GetPatch()
1581 path = "/".join(tf.name.split("/")[:-1])
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001582 if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001583 path not in known_paths:
1584 # patch is almost as big as the file; don't bother patching
1585 # or a patch + rename cannot take place due to the target
1586 # directory not existing
1587 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001588 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001589 if sf.name in renames:
1590 del renames[sf.name]
1591 AddToKnownPaths(tf.name, known_paths)
1592 else:
1593 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1594 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1595 largest_source_size = max(largest_source_size, sf.size)
1596
1597 self.largest_source_size = largest_source_size
1598
1599 def EmitVerification(self, script):
1600 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001601 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001602 if tf.name != sf.name:
1603 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1604 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1605 so_far += sf.size
1606 return so_far
1607
Michael Runge63f01de2014-10-28 19:24:19 -07001608 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001609 for fn, _, sha1 in self.verbatim_targets:
1610 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001611 script.FileCheck("/"+fn, sha1)
1612 for tf, _, _, _ in self.patch_list:
1613 script.FileCheck(tf.name, tf.sha1)
1614
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001615 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001616 file_list = ["/" + i[0] for i in self.verbatim_targets]
1617 file_list += ["/" + i for i in self.source_data
1618 if i not in self.target_data and i not in self.renames]
1619 file_list += list(extras)
1620 # Sort the list in descending order, which removes all the files first
1621 # before attempting to remove the folder. (Bug: 22960996)
1622 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001623
1624 def TotalPatchSize(self):
1625 return sum(i[1].size for i in self.patch_list)
1626
1627 def EmitPatches(self, script, total_patch_size, so_far):
1628 self.deferred_patch_list = deferred_patch_list = []
1629 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001630 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001631 if tf.name == "system/build.prop":
1632 deferred_patch_list.append(item)
1633 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001634 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001635 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001636 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1637 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001638 so_far += tf.size
1639 script.SetProgress(so_far / total_patch_size)
1640 return so_far
1641
1642 def EmitDeferredPatches(self, script):
1643 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001644 tf, sf, _, _ = item
1645 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1646 "patch/" + sf.name + ".p")
1647 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001648
1649 def EmitRenames(self, script):
1650 if len(self.renames) > 0:
1651 script.Print("Renaming files...")
1652 for src, tgt in self.renames.iteritems():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001653 print("Renaming " + src + " to " + tgt.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001654 script.RenameFile(src, tgt.name)
1655
1656
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001657def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001658 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1659 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1660
Doug Zongker26e66192014-02-20 13:22:07 -08001661 if (OPTIONS.block_based and
1662 target_has_recovery_patch and
1663 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001664 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1665
Doug Zongker37974732010-09-16 17:44:38 -07001666 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1667 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001668
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001669 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001670 print("WARNING: generating edify script for a source that "
1671 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001672 script = edify_generator.EdifyGenerator(
1673 source_version, OPTIONS.target_info_dict,
1674 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001675
Tao Bao34b47bf2015-06-22 19:17:41 -07001676 recovery_mount_options = OPTIONS.source_info_dict.get(
1677 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001678 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1679 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001680 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -07001681 if source_oem_props or target_oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001682 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -07001683
Dan Albert8b72aef2015-03-23 19:13:21 -07001684 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001685 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001686 oem_dicts and oem_dicts[0],
1687 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001688 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001689 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001690
Tao Baob31892e2017-02-07 11:21:17 -08001691 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -08001692
Doug Zongker05d3dea2009-06-22 11:32:31 -07001693 device_specific = common.DeviceSpecificParams(
1694 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001695 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001696 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001697 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001698 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001699 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001700 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001701 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001702
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001703 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001704 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001705 if HasVendorPartition(target_zip):
1706 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001707 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001708 else:
1709 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001710
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001711 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001712 OPTIONS.target_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001713 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001714 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001715
Tao Bao3e30d972016-03-15 13:20:19 -07001716 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001717 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001718 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001719 script.AssertSomeThumbprint(
1720 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1721 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001722 elif source_oem_props is None and target_oem_props is not None:
1723 script.AssertFingerprintOrThumbprint(
1724 source_fp,
1725 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1726 else:
1727 script.AssertFingerprintOrThumbprint(
1728 target_fp,
1729 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001730
Doug Zongker2ea21062010-04-28 16:05:21 -07001731 metadata["pre-build"] = source_fp
1732 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001733 metadata["pre-build-incremental"] = GetBuildProp(
1734 "ro.build.version.incremental", OPTIONS.source_info_dict)
1735 metadata["post-build-incremental"] = GetBuildProp(
1736 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001737
Doug Zongker55d93282011-01-25 17:03:34 -08001738 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001739 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1740 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001741 target_boot = common.GetBootableImage(
1742 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001743 updating_boot = (not OPTIONS.two_step and
1744 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001745
Doug Zongker55d93282011-01-25 17:03:34 -08001746 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001747 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1748 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001749 target_recovery = common.GetBootableImage(
1750 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001751 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001752
Doug Zongker881dd402009-09-20 14:03:55 -07001753 # Here's how we divide up the progress bar:
1754 # 0.1 for verifying the start state (PatchCheck calls)
1755 # 0.8 for applying patches (ApplyPatch calls)
1756 # 0.1 for unpacking verbatim files, symlinking, and doing the
1757 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001758
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001759 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001760 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001761
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001762 # Two-step incremental package strategy (in chronological order,
1763 # which is *not* the order in which the generated script has
1764 # things):
1765 #
1766 # if stage is not "2/3" or "3/3":
1767 # do verification on current system
1768 # write recovery image to boot partition
1769 # set stage to "2/3"
1770 # reboot to boot partition and restart recovery
1771 # else if stage is "2/3":
1772 # write recovery image to recovery partition
1773 # set stage to "3/3"
1774 # reboot to recovery partition and restart recovery
1775 # else:
1776 # (stage must be "3/3")
1777 # perform update:
1778 # patch system files, etc.
1779 # force full install of new boot image
1780 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001781 # complete script normally
1782 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001783
1784 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001785 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001786 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001787 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001788 assert fs.fs_type.upper() == "EMMC", \
1789 "two-step packages only supported on devices with EMMC /misc partitions"
1790 bcb_dev = {"bcb_dev": fs.device}
1791 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1792 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001793if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001794""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001795
1796 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1797 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001798 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001799 script.WriteRawImage("/recovery", "recovery.img")
1800 script.AppendExtra("""
1801set_stage("%(bcb_dev)s", "3/3");
1802reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001803else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001804""" % bcb_dev)
1805
Tao Baod42e97e2016-11-30 12:11:57 -08001806 # Stage 1/3: (a) Verify the current system.
1807 script.Comment("Stage 1/3")
1808
Tao Bao6c55a8a2015-04-08 15:30:27 -07001809 # Dump fingerprints
1810 script.Print("Source: %s" % (source_fp,))
1811 script.Print("Target: %s" % (target_fp,))
1812
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001813 script.Print("Verifying current system...")
1814
Doug Zongkere5ff5902012-01-17 10:55:37 -08001815 device_specific.IncrementalOTA_VerifyBegin()
1816
Doug Zongker881dd402009-09-20 14:03:55 -07001817 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001818 so_far = system_diff.EmitVerification(script)
1819 if vendor_diff:
1820 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001821
Tao Baod8d14be2016-02-04 14:26:02 -08001822 size = []
1823 if system_diff.patch_list:
1824 size.append(system_diff.largest_source_size)
1825 if vendor_diff:
1826 if vendor_diff.patch_list:
1827 size.append(vendor_diff.largest_source_size)
1828
Doug Zongker5da317e2009-06-02 13:38:17 -07001829 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001830 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001831 _, _, d = d.ComputePatch()
Tao Bao89fbb0f2017-01-10 10:47:58 -08001832 print("boot target: %d source: %d diff: %d" % (
1833 target_boot.size, source_boot.size, len(d)))
Doug Zongker5da317e2009-06-02 13:38:17 -07001834
Doug Zongker048e7ca2009-06-15 14:31:53 -07001835 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001836
Tao Baodd24da92015-07-29 14:09:23 -07001837 boot_type, boot_device = common.GetTypeAndDevice(
1838 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001839
1840 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1841 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001842 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001843 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001844 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001845 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001846
Tao Baod8d14be2016-02-04 14:26:02 -08001847 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001848 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001849
Doug Zongker05d3dea2009-06-22 11:32:31 -07001850 device_specific.IncrementalOTA_VerifyEnd()
1851
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001852 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001853 # Stage 1/3: (b) Write recovery image to /boot.
1854 _WriteRecoveryImageToBoot(script, output_zip)
1855
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001856 script.AppendExtra("""
1857set_stage("%(bcb_dev)s", "2/3");
1858reboot_now("%(bcb_dev)s", "");
1859else
1860""" % bcb_dev)
1861
Tao Baod42e97e2016-11-30 12:11:57 -08001862 # Stage 3/3: Make changes.
1863 script.Comment("Stage 3/3")
1864
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001865 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001866
Doug Zongkere5ff5902012-01-17 10:55:37 -08001867 device_specific.IncrementalOTA_InstallBegin()
1868
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001869 if OPTIONS.two_step:
1870 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1871 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001872 print("writing full boot image (forced by two-step mode)")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001873
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001874 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001875 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1876 if vendor_diff:
1877 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001878
Doug Zongker881dd402009-09-20 14:03:55 -07001879 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001880 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1881 if vendor_diff:
1882 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001883 if updating_boot:
1884 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001885
1886 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001887 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1888 if vendor_diff:
1889 script.Print("Patching vendor files...")
1890 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001891
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001892 if not OPTIONS.two_step:
1893 if updating_boot:
1894 # Produce the boot image by applying a patch to the current
1895 # contents of the boot partition, and write it back to the
1896 # partition.
1897 script.Print("Patching boot image...")
1898 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1899 % (boot_type, boot_device,
1900 source_boot.size, source_boot.sha1,
1901 target_boot.size, target_boot.sha1),
1902 "-",
1903 target_boot.size, target_boot.sha1,
1904 source_boot.sha1, "patch/boot.img.p")
1905 so_far += target_boot.size
1906 script.SetProgress(so_far / total_patch_size)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001907 print("boot image changed; including.")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001908 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001909 print("boot image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001910
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001911 system_items = ItemSet("system", "META/filesystem_config.txt")
1912 if vendor_diff:
1913 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1914
Doug Zongkereef39442009-04-02 12:14:19 -07001915 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001916 # Recovery is generated as a patch using both the boot image
1917 # (which contains the same linux kernel as recovery) and the file
1918 # /system/etc/recovery-resource.dat (which contains all the images
1919 # used in the recovery UI) as sources. This lets us minimize the
1920 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001921 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001922 # For older builds where recovery-resource.dat is not present, we
1923 # use only the boot image as the source.
1924
Doug Zongkerc9253822014-02-04 12:17:58 -08001925 if not target_has_recovery_patch:
1926 def output_sink(fn, data):
1927 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001928 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001929
1930 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1931 target_recovery, target_boot)
1932 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001933 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001934 "/system/etc/install-recovery.sh"])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001935 print("recovery image changed; including as patch from boot.")
Doug Zongkereef39442009-04-02 12:14:19 -07001936 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001937 print("recovery image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001938
Doug Zongker881dd402009-09-20 14:03:55 -07001939 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001940
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001941 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1942 if vendor_diff:
1943 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1944
1945 temp_script = script.MakeTemporary()
1946 system_items.GetMetadata(target_zip)
1947 system_items.Get("system").SetPermissions(temp_script)
1948 if vendor_diff:
1949 vendor_items.GetMetadata(target_zip)
1950 vendor_items.Get("vendor").SetPermissions(temp_script)
1951
1952 # Note that this call will mess up the trees of Items, so make sure
1953 # we're done with them.
1954 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1955 if vendor_diff:
1956 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001957
1958 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001959 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1960
1961 # Delete all the symlinks in source that aren't in target. This
1962 # needs to happen before verbatim files are unpacked, in case a
1963 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001964
1965 # If a symlink in the source will be replaced by a regular file, we cannot
1966 # delete the symlink/file in case the package gets applied again. For such
1967 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1968 # (Bug: 23646151)
1969 replaced_symlinks = dict()
1970 if system_diff:
1971 for i in system_diff.verbatim_targets:
1972 replaced_symlinks["/%s" % (i[0],)] = i[2]
1973 if vendor_diff:
1974 for i in vendor_diff.verbatim_targets:
1975 replaced_symlinks["/%s" % (i[0],)] = i[2]
1976
1977 if system_diff:
1978 for tf in system_diff.renames.values():
1979 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1980 if vendor_diff:
1981 for tf in vendor_diff.renames.values():
1982 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1983
1984 always_delete = []
1985 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001986 for dest, link in source_symlinks:
1987 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001988 if link in replaced_symlinks:
1989 may_delete.append((link, replaced_symlinks[link]))
1990 else:
1991 always_delete.append(link)
1992 script.DeleteFiles(always_delete)
1993 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001994
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001995 if system_diff.verbatim_targets:
1996 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001997 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001998 if vendor_diff and vendor_diff.verbatim_targets:
1999 script.Print("Unpacking new vendor files...")
2000 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002001
Doug Zongkerc9253822014-02-04 12:17:58 -08002002 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08002003 script.Print("Unpacking new recovery...")
2004 script.UnpackPackageDir("recovery", "/system")
2005
Doug Zongkerc8b4e842014-06-16 15:16:31 -07002006 system_diff.EmitRenames(script)
2007 if vendor_diff:
2008 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08002009
Doug Zongker05d3dea2009-06-22 11:32:31 -07002010 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07002011
2012 # Create all the symlinks that don't already exist, or point to
2013 # somewhere different than what we want. Delete each symlink before
2014 # creating it, since the 'symlink' command won't overwrite.
2015 to_create = []
2016 for dest, link in target_symlinks:
2017 if link in source_symlinks_d:
2018 if dest != source_symlinks_d[link]:
2019 to_create.append((dest, link))
2020 else:
2021 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002022 script.DeleteFiles([i[1] for i in to_create])
2023 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07002024
2025 # Now that the symlinks are created, we can set all the
2026 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002027 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07002028
Doug Zongker881dd402009-09-20 14:03:55 -07002029 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07002030 device_specific.IncrementalOTA_InstallEnd()
2031
Doug Zongker1c390a22009-05-14 19:06:36 -07002032 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07002033 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07002034
Doug Zongkere92f15a2011-08-26 13:46:40 -07002035 # Patch the build.prop file last, so if something fails but the
2036 # device can still come up, it appears to be the old build and will
2037 # get set the OTA package again to retry.
2038 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07002039 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07002040
Doug Zongker922206e2014-03-04 13:16:24 -08002041 if OPTIONS.wipe_user_data:
2042 script.Print("Erasing user data...")
2043 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08002044 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08002045
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002046 if OPTIONS.two_step:
2047 script.AppendExtra("""
2048set_stage("%(bcb_dev)s", "");
2049endif;
2050endif;
2051""" % bcb_dev)
2052
Michael Runge63f01de2014-10-28 19:24:19 -07002053 if OPTIONS.verify and system_diff:
2054 script.Print("Remounting and verifying system partition files...")
2055 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08002056 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002057 system_diff.EmitExplicitTargetVerification(script)
2058
2059 if OPTIONS.verify and vendor_diff:
2060 script.Print("Remounting and verifying vendor partition files...")
2061 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08002062 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002063 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08002064
2065 # For downgrade OTAs, we prefer to use the update-binary in the source
2066 # build that is actually newer than the one in the target build.
2067 if OPTIONS.downgrade:
2068 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
2069 else:
2070 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07002071
Tao Baod8d14be2016-02-04 14:26:02 -08002072 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07002073 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07002074
2075
2076def main(argv):
2077
2078 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08002079 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002080 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07002081 elif o in ("-k", "--package_key"):
2082 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002083 elif o in ("-i", "--incremental_from"):
2084 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002085 elif o == "--full_radio":
2086 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002087 elif o == "--full_bootloader":
2088 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002089 elif o in ("-w", "--wipe_user_data"):
2090 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002091 elif o == "--downgrade":
2092 OPTIONS.downgrade = True
2093 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002094 elif o == "--override_timestamp":
2095 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07002096 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002097 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002098 elif o == "--oem_no_mount":
2099 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002100 elif o in ("-e", "--extra_script"):
2101 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002102 elif o in ("-t", "--worker_threads"):
2103 if a.isdigit():
2104 OPTIONS.worker_threads = int(a)
2105 else:
2106 raise ValueError("Cannot parse value %r for option %r - only "
2107 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002108 elif o in ("-2", "--two_step"):
2109 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002110 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002111 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002112 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002113 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002114 elif o == "--block":
2115 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002116 elif o in ("-b", "--binary"):
2117 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002118 elif o in ("--no_fallback_to_full",):
2119 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002120 elif o == "--stash_threshold":
2121 try:
2122 OPTIONS.stash_threshold = float(a)
2123 except ValueError:
2124 raise ValueError("Cannot parse value %r for option %r - expecting "
2125 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002126 elif o == "--gen_verify":
2127 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002128 elif o == "--log_diff":
2129 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002130 elif o == "--payload_signer":
2131 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002132 elif o == "--payload_signer_args":
2133 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002134 else:
2135 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002136 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002137
2138 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08002139 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002140 extra_long_opts=[
2141 "board_config=",
2142 "package_key=",
2143 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002144 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002145 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002146 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002147 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002148 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002149 "extra_script=",
2150 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002151 "two_step",
2152 "no_signing",
2153 "block",
2154 "binary=",
2155 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002156 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002157 "verify",
2158 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002159 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002160 "gen_verify",
2161 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002162 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002163 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002164 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002165
2166 if len(args) != 2:
2167 common.Usage(__doc__)
2168 sys.exit(1)
2169
Tao Bao5d182562016-02-23 11:38:39 -08002170 if OPTIONS.downgrade:
2171 # Sanity check to enforce a data wipe.
2172 if not OPTIONS.wipe_user_data:
2173 raise ValueError("Cannot downgrade without a data wipe")
2174
2175 # We should only allow downgrading incrementals (as opposed to full).
2176 # Otherwise the device may go back from arbitrary build with this full
2177 # OTA package.
2178 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002179 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002180
Tao Bao3e6161a2017-02-28 11:48:48 -08002181 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
2182 "Cannot have --downgrade AND --override_timestamp both"
2183
Tao Baoc098e9e2016-01-07 13:03:56 -08002184 # Load the dict file from the zip directly to have a peek at the OTA type.
2185 # For packages using A/B update, unzipping is not needed.
2186 input_zip = zipfile.ZipFile(args[0], "r")
2187 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2188 common.ZipClose(input_zip)
2189
2190 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2191
2192 if ab_update:
2193 if OPTIONS.incremental_source is not None:
2194 OPTIONS.target_info_dict = OPTIONS.info_dict
2195 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2196 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2197 common.ZipClose(source_zip)
2198
2199 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002200 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002201 common.DumpInfoDict(OPTIONS.info_dict)
2202
2203 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002204 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002205 common.DumpInfoDict(OPTIONS.source_info_dict)
2206
2207 WriteABOTAPackageWithBrilloScript(
2208 target_file=args[0],
2209 output_file=args[1],
2210 source_file=OPTIONS.incremental_source)
2211
Tao Bao89fbb0f2017-01-10 10:47:58 -08002212 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002213 return
2214
Doug Zongker1c390a22009-05-14 19:06:36 -07002215 if OPTIONS.extra_script is not None:
2216 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2217
Tao Bao89fbb0f2017-01-10 10:47:58 -08002218 print("unzipping target target-files...")
Tao Bao6b0b2f92017-03-05 11:38:11 -08002219 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
2220 args[0], UNZIP_PATTERN if OPTIONS.block_based else None)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002221
Doug Zongkereef39442009-04-02 12:14:19 -07002222 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002223 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002224
Doug Zongker37974732010-09-16 17:44:38 -07002225 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002226 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07002227 common.DumpInfoDict(OPTIONS.info_dict)
2228
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002229 # If the caller explicitly specified the device-specific extensions
2230 # path via -s/--device_specific, use that. Otherwise, use
2231 # META/releasetools.py if it is present in the target target_files.
2232 # Otherwise, take the path of the file from 'tool_extensions' in the
2233 # info dict and look for that in the local filesystem, relative to
2234 # the current directory.
2235
Doug Zongker37974732010-09-16 17:44:38 -07002236 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002237 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2238 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08002239 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002240 OPTIONS.device_specific = from_input
2241 else:
2242 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2243
Doug Zongker37974732010-09-16 17:44:38 -07002244 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002245 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002246
Tao Baoc098e9e2016-01-07 13:03:56 -08002247 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002248 raise common.ExternalError(
2249 "--- target build has specified no recovery ---")
2250
Tao Bao767e3ac2015-11-10 12:19:19 -08002251 # Use the default key to sign the package if not specified with package_key.
2252 if not OPTIONS.no_signing:
2253 if OPTIONS.package_key is None:
2254 OPTIONS.package_key = OPTIONS.info_dict.get(
2255 "default_system_dev_certificate",
2256 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002257
Tao Bao767e3ac2015-11-10 12:19:19 -08002258 # Set up the output zip. Create a temporary zip file if signing is needed.
2259 if OPTIONS.no_signing:
2260 if os.path.exists(args[1]):
2261 os.unlink(args[1])
2262 output_zip = zipfile.ZipFile(args[1], "w",
2263 compression=zipfile.ZIP_DEFLATED)
2264 else:
2265 temp_zip_file = tempfile.NamedTemporaryFile()
2266 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2267 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002268
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002269 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002270 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002271 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002272 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002273 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002274
Tao Bao9bc6bb22015-11-09 16:58:28 -08002275 # Generate a verify package.
2276 if OPTIONS.gen_verify:
2277 WriteVerifyPackage(input_zip, output_zip)
2278
Tao Bao767e3ac2015-11-10 12:19:19 -08002279 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002280 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002281 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002282
2283 # Generate an incremental OTA. It will fall back to generate a full OTA on
2284 # failure unless no_fallback_to_full is specified.
2285 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002286 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08002287 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08002288 OPTIONS.incremental_source,
2289 UNZIP_PATTERN if OPTIONS.block_based else None)
Tao Bao767e3ac2015-11-10 12:19:19 -08002290 OPTIONS.target_info_dict = OPTIONS.info_dict
2291 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2292 OPTIONS.source_tmp)
2293 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002294 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002295 common.DumpInfoDict(OPTIONS.source_info_dict)
2296 try:
2297 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002298 if OPTIONS.log_diff:
2299 out_file = open(OPTIONS.log_diff, 'w')
2300 import target_files_diff
2301 target_files_diff.recursiveDiff('',
2302 OPTIONS.source_tmp,
2303 OPTIONS.input_tmp,
2304 out_file)
2305 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002306 except ValueError:
2307 if not OPTIONS.fallback_to_full:
2308 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08002309 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002310 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002311 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002312
Tao Bao767e3ac2015-11-10 12:19:19 -08002313 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002314
Tao Bao767e3ac2015-11-10 12:19:19 -08002315 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002316 if not OPTIONS.no_signing:
2317 SignOutput(temp_zip_file.name, args[1])
2318 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002319
Tao Bao89fbb0f2017-01-10 10:47:58 -08002320 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002321
2322
2323if __name__ == '__main__':
2324 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002325 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002326 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002327 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002328 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002329 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002330 finally:
2331 common.Cleanup()