blob: cfe389ea3bfffa0d6d6d7c1f3964546622396e82 [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
Tao Bao7e0f1602017-03-06 15:50:08 -0800560def GetImage(which, tmpdir):
561 """Returns an image object suitable for passing to BlockImageDiff.
562
563 'which' partition must be "system" or "vendor". A prebuilt image and file
564 map must already exist in tmpdir.
565 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700566
567 assert which in ("system", "vendor")
568
569 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700570 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
Doug Zongker3c84f562014-07-31 11:06:30 -0700571
Tao Bao7e0f1602017-03-06 15:50:08 -0800572 # The image and map files must have been created prior to calling
573 # ota_from_target_files.py (since LMP).
574 assert os.path.exists(path) and os.path.exists(mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700575
Tao Baoff777812015-05-12 11:42:31 -0700576 # Bug: http://b/20939131
577 # In ext4 filesystems, block 0 might be changed even being mounted
578 # R/O. We add it to clobbered_blocks so that it will be written to the
579 # target unconditionally. Note that they are still part of care_map.
580 clobbered_blocks = "0"
581
582 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700583
584
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700585def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700586 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700587 # be installed on top of. For now, we expect the API just won't
588 # change very often. Similarly for fstab, it might have changed
589 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700590 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700591
Tao Bao838c68f2016-03-15 19:16:18 +0000592 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700593 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800594 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700595 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800596 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -0700597
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800598 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
599 OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700600 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800601 "pre-device": GetOemProperty("ro.product.device", oem_props,
602 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -0700603 OPTIONS.info_dict),
604 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
605 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700606
Doug Zongker05d3dea2009-06-22 11:32:31 -0700607 device_specific = common.DeviceSpecificParams(
608 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700609 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700610 output_zip=output_zip,
611 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700612 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700613 metadata=metadata,
614 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700615
Doug Zongkerc9253822014-02-04 12:17:58 -0800616 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800617 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800618
Tao Baod8d14be2016-02-04 14:26:02 -0800619 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
620
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700621 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
622 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
623 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700624
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800625 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700626 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800627
628 # Two-step package strategy (in chronological order, which is *not*
629 # the order in which the generated script has things):
630 #
631 # if stage is not "2/3" or "3/3":
632 # write recovery image to boot partition
633 # set stage to "2/3"
634 # reboot to boot partition and restart recovery
635 # else if stage is "2/3":
636 # write recovery image to recovery partition
637 # set stage to "3/3"
638 # reboot to recovery partition and restart recovery
639 # else:
640 # (stage must be "3/3")
641 # set stage to ""
642 # do normal full package installation:
643 # wipe and install system, boot image, etc.
644 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700645 # complete script normally
646 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800647
648 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
649 OPTIONS.input_tmp, "RECOVERY")
650 if OPTIONS.two_step:
651 if not OPTIONS.info_dict.get("multistage_support", None):
652 assert False, "two-step packages not supported by this build"
653 fs = OPTIONS.info_dict["fstab"]["/misc"]
654 assert fs.fs_type.upper() == "EMMC", \
655 "two-step packages only supported on devices with EMMC /misc partitions"
656 bcb_dev = {"bcb_dev": fs.device}
657 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
658 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700659if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800660""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800661
662 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
663 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800664 script.WriteRawImage("/recovery", "recovery.img")
665 script.AppendExtra("""
666set_stage("%(bcb_dev)s", "3/3");
667reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700668else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800669""" % bcb_dev)
670
Tao Baod42e97e2016-11-30 12:11:57 -0800671 # Stage 3/3: Make changes.
672 script.Comment("Stage 3/3")
673
Tao Bao6c55a8a2015-04-08 15:30:27 -0700674 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700675 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700676
Doug Zongkere5ff5902012-01-17 10:55:37 -0800677 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700678
Doug Zongker01ce19c2014-02-04 13:48:15 -0800679 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700680
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700681 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800682 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700683 if HasVendorPartition(input_zip):
684 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700685
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400686 # Place a copy of file_contexts.bin into the OTA package which will be used
687 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700688 if "selinux_fc" in OPTIONS.info_dict:
689 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500690
Michael Runge7cd99ba2014-10-22 17:21:48 -0700691 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
692
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700693 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700694 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800695
Doug Zongker26e66192014-02-20 13:22:07 -0800696 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700697 # Full OTA is done as an "incremental" against an empty source
698 # image. This has the effect of writing new data from the package
699 # to the entire partition, but lets us reuse the updater code that
700 # writes incrementals to do it.
Tao Bao7e0f1602017-03-06 15:50:08 -0800701 system_tgt = GetImage("system", OPTIONS.input_tmp)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700702 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700703 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700704 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800705 else:
706 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700707 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800708 if not has_recovery_patch:
709 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800710 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700711
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700712 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800713 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700714
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700715 boot_img = common.GetBootableImage(
716 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800717
Doug Zongker91a99c22014-05-09 13:15:01 -0700718 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800719 def output_sink(fn, data):
720 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700721 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800722
723 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
724 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700725
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700726 system_items.GetMetadata(input_zip)
727 system_items.Get("system").SetPermissions(script)
728
729 if HasVendorPartition(input_zip):
730 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
731 script.ShowProgress(0.1, 0)
732
733 if block_based:
Tao Bao7e0f1602017-03-06 15:50:08 -0800734 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700735 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700736 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700737 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700738 else:
739 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700740 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700741 script.UnpackPackageDir("vendor", "/vendor")
742
743 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
744 script.MakeSymlinks(symlinks)
745
746 vendor_items.GetMetadata(input_zip)
747 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700748
Doug Zongker37974732010-09-16 17:44:38 -0700749 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700750 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700751
Doug Zongker01ce19c2014-02-04 13:48:15 -0800752 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700753 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700754
Doug Zongker01ce19c2014-02-04 13:48:15 -0800755 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700756 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700757
Doug Zongker1c390a22009-05-14 19:06:36 -0700758 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700759 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700760
Doug Zongker14833602010-02-02 13:12:04 -0800761 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800762
Doug Zongker922206e2014-03-04 13:16:24 -0800763 if OPTIONS.wipe_user_data:
764 script.ShowProgress(0.1, 10)
765 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700766
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800767 if OPTIONS.two_step:
768 script.AppendExtra("""
769set_stage("%(bcb_dev)s", "");
770""" % bcb_dev)
771 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800772
773 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
774 script.Comment("Stage 1/3")
775 _WriteRecoveryImageToBoot(script, output_zip)
776
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800777 script.AppendExtra("""
778set_stage("%(bcb_dev)s", "2/3");
779reboot_now("%(bcb_dev)s", "");
780endif;
781endif;
782""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800783
Tao Bao5d182562016-02-23 11:38:39 -0800784 script.SetProgress(1)
785 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800786 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700787 WriteMetadata(metadata, output_zip)
788
Doug Zongkerfc44a512014-08-26 13:10:25 -0700789
Dan Albert8e0178d2015-01-27 15:53:15 -0800790def WritePolicyConfig(file_name, output_zip):
791 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500792
Doug Zongker2ea21062010-04-28 16:05:21 -0700793
794def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800795 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
796 common.ZipWriteStr(output_zip, METADATA_NAME, value,
797 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700798
Doug Zongkerfc44a512014-08-26 13:10:25 -0700799
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700800def LoadPartitionFiles(z, partition):
801 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700802 ZipFile, and return a dict of {filename: File object}."""
803 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700804 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700805 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700806 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700807 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700808 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700809 data = z.read(info.filename)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +0900810 out[fn] = common.File(fn, data, info.compress_size)
Doug Zongker1807e702012-02-28 12:21:08 -0800811 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700812
813
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700814def GetBuildProp(prop, info_dict):
815 """Return the fingerprint of the build of a given target-files info_dict."""
816 try:
817 return info_dict.get("build.prop", {})[prop]
818 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700819 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700820
Doug Zongkerfc44a512014-08-26 13:10:25 -0700821
Michael Runge4038aa82013-12-13 18:06:28 -0800822def AddToKnownPaths(filename, known_paths):
823 if filename[-1] == "/":
824 return
825 dirs = filename.split("/")[:-1]
826 while len(dirs) > 0:
827 path = "/".join(dirs)
828 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700829 break
Michael Runge4038aa82013-12-13 18:06:28 -0800830 known_paths.add(path)
831 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700832
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700833
Tao Baob31892e2017-02-07 11:21:17 -0800834def HandleDowngradeMetadata(metadata):
835 # Only incremental OTAs are allowed to reach here.
836 assert OPTIONS.incremental_source is not None
837
838 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
839 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
840 is_downgrade = long(post_timestamp) < long(pre_timestamp)
841
842 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800843 if not is_downgrade:
844 raise RuntimeError("--downgrade specified but no downgrade detected: "
845 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800846 metadata["ota-downgrade"] = "yes"
847 elif OPTIONS.timestamp:
848 if not is_downgrade:
849 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
850 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
851 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800852 else:
853 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800854 raise RuntimeError("Downgrade detected based on timestamp check: "
855 "pre: %s, post: %s. Need to specify --timestamp OR "
856 "--downgrade to allow building the incremental." % (
857 pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800858 metadata["post-timestamp"] = post_timestamp
859
860
Geremy Condra36bd3652014-02-06 19:45:10 -0800861def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700862 # TODO(tbao): We should factor out the common parts between
863 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800864 source_version = OPTIONS.source_info_dict["recovery_api_version"]
865 target_version = OPTIONS.target_info_dict["recovery_api_version"]
866
867 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700868 print("WARNING: generating edify script for a source that "
869 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700870 script = edify_generator.EdifyGenerator(
871 source_version, OPTIONS.target_info_dict,
872 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800873
Tao Bao3806c232015-07-05 21:08:33 -0700874 recovery_mount_options = OPTIONS.source_info_dict.get(
875 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700876 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
877 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800878 oem_dicts = None
879 if source_oem_props and target_oem_props:
880 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700881
Dan Albert8b72aef2015-03-23 19:13:21 -0700882 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700883 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800884 oem_dicts and oem_dicts[0],
885 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800886 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700887 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800888
Tao Baob31892e2017-02-07 11:21:17 -0800889 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800890
Geremy Condra36bd3652014-02-06 19:45:10 -0800891 device_specific = common.DeviceSpecificParams(
892 source_zip=source_zip,
893 source_version=source_version,
894 target_zip=target_zip,
895 target_version=target_version,
896 output_zip=output_zip,
897 script=script,
898 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700899 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800900
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800901 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700902 OPTIONS.source_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800903 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700904 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800905 metadata["pre-build"] = source_fp
906 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700907 metadata["pre-build-incremental"] = GetBuildProp(
908 "ro.build.version.incremental", OPTIONS.source_info_dict)
909 metadata["post-build-incremental"] = GetBuildProp(
910 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800911
912 source_boot = common.GetBootableImage(
913 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
914 OPTIONS.source_info_dict)
915 target_boot = common.GetBootableImage(
916 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
917 updating_boot = (not OPTIONS.two_step and
918 (source_boot.data != target_boot.data))
919
Geremy Condra36bd3652014-02-06 19:45:10 -0800920 target_recovery = common.GetBootableImage(
921 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800922
Tao Bao7e0f1602017-03-06 15:50:08 -0800923 system_src = GetImage("system", OPTIONS.source_tmp)
924 system_tgt = GetImage("system", OPTIONS.target_tmp)
Tao Baodd2a5892015-03-12 12:32:37 -0700925
926 blockimgdiff_version = 1
927 if OPTIONS.info_dict:
928 blockimgdiff_version = max(
929 int(i) for i in
930 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
931
Tao Baof8acad12016-07-07 09:09:58 -0700932 # Check the first block of the source system partition for remount R/W only
933 # if the filesystem is ext4.
934 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
935 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700936 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
937 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
938 # b) the blocks listed in block map may not contain all the bytes for a given
939 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700940 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
941 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
942 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700943 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800944 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700945 version=blockimgdiff_version,
946 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700947
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700948 if HasVendorPartition(target_zip):
949 if not HasVendorPartition(source_zip):
950 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Bao7e0f1602017-03-06 15:50:08 -0800951 vendor_src = GetImage("vendor", OPTIONS.source_tmp)
952 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800953
954 # Check first block of vendor partition for remount R/W only if
955 # disk type is ext4
956 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800957 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700958 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700959 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800960 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700961 version=blockimgdiff_version,
962 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700963 else:
964 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800965
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800966 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800967 device_specific.IncrementalOTA_Assertions()
968
969 # Two-step incremental package strategy (in chronological order,
970 # which is *not* the order in which the generated script has
971 # things):
972 #
973 # if stage is not "2/3" or "3/3":
974 # do verification on current system
975 # write recovery image to boot partition
976 # set stage to "2/3"
977 # reboot to boot partition and restart recovery
978 # else if stage is "2/3":
979 # write recovery image to recovery partition
980 # set stage to "3/3"
981 # reboot to recovery partition and restart recovery
982 # else:
983 # (stage must be "3/3")
984 # perform update:
985 # patch system files, etc.
986 # force full install of new boot image
987 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700988 # complete script normally
989 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800990
991 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700992 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800993 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700994 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800995 assert fs.fs_type.upper() == "EMMC", \
996 "two-step packages only supported on devices with EMMC /misc partitions"
997 bcb_dev = {"bcb_dev": fs.device}
998 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
999 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001000if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001001""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001002
1003 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1004 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001005 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001006 script.WriteRawImage("/recovery", "recovery.img")
1007 script.AppendExtra("""
1008set_stage("%(bcb_dev)s", "3/3");
1009reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001010else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001011""" % bcb_dev)
1012
Tao Baod42e97e2016-11-30 12:11:57 -08001013 # Stage 1/3: (a) Verify the current system.
1014 script.Comment("Stage 1/3")
1015
Tao Bao6c55a8a2015-04-08 15:30:27 -07001016 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -08001017 script.Print("Source: %s" % (source_fp,))
1018 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001019
Geremy Condra36bd3652014-02-06 19:45:10 -08001020 script.Print("Verifying current system...")
1021
1022 device_specific.IncrementalOTA_VerifyBegin()
1023
Tao Bao3e30d972016-03-15 13:20:19 -07001024 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1025 # patching on a device that's already on the target build will damage the
1026 # system. Because operations like move don't check the block state, they
1027 # always apply the changes unconditionally.
1028 if blockimgdiff_version <= 2:
1029 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001030 script.AssertSomeFingerprint(source_fp)
1031 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001032 script.AssertSomeThumbprint(
1033 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001034
1035 else: # blockimgdiff_version > 2
1036 if source_oem_props is None and target_oem_props is None:
1037 script.AssertSomeFingerprint(source_fp, target_fp)
1038 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001039 script.AssertSomeThumbprint(
1040 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1041 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001042 elif source_oem_props is None and target_oem_props is not None:
1043 script.AssertFingerprintOrThumbprint(
1044 source_fp,
1045 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1046 else:
1047 script.AssertFingerprintOrThumbprint(
1048 target_fp,
1049 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001050
Tao Baod8d14be2016-02-04 14:26:02 -08001051 # Check the required cache size (i.e. stashed blocks).
1052 size = []
1053 if system_diff:
1054 size.append(system_diff.required_cache)
1055 if vendor_diff:
1056 size.append(vendor_diff.required_cache)
1057
Geremy Condra36bd3652014-02-06 19:45:10 -08001058 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001059 boot_type, boot_device = common.GetTypeAndDevice(
1060 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001061 d = common.Difference(target_boot, source_boot)
1062 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001063 if d is None:
1064 include_full_boot = True
1065 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1066 else:
1067 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001068
Tao Bao89fbb0f2017-01-10 10:47:58 -08001069 print("boot target: %d source: %d diff: %d" % (
1070 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001071
Doug Zongkerf8340082014-08-05 10:39:37 -07001072 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001073
Doug Zongkerf8340082014-08-05 10:39:37 -07001074 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1075 (boot_type, boot_device,
1076 source_boot.size, source_boot.sha1,
1077 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001078 size.append(target_boot.size)
1079
1080 if size:
1081 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001082
1083 device_specific.IncrementalOTA_VerifyEnd()
1084
1085 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001086 # Stage 1/3: (b) Write recovery image to /boot.
1087 _WriteRecoveryImageToBoot(script, output_zip)
1088
Geremy Condra36bd3652014-02-06 19:45:10 -08001089 script.AppendExtra("""
1090set_stage("%(bcb_dev)s", "2/3");
1091reboot_now("%(bcb_dev)s", "");
1092else
1093""" % bcb_dev)
1094
Tao Baod42e97e2016-11-30 12:11:57 -08001095 # Stage 3/3: Make changes.
1096 script.Comment("Stage 3/3")
1097
Jesse Zhao75bcea02015-01-06 10:59:53 -08001098 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001099 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001100 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001101 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001102
Geremy Condra36bd3652014-02-06 19:45:10 -08001103 script.Comment("---- start making changes here ----")
1104
1105 device_specific.IncrementalOTA_InstallBegin()
1106
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001107 system_diff.WriteScript(script, output_zip,
1108 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001109
Doug Zongkerfc44a512014-08-26 13:10:25 -07001110 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001111 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001112
1113 if OPTIONS.two_step:
1114 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1115 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001116 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001117
1118 if not OPTIONS.two_step:
1119 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001120 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001121 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001122 script.Print("Installing boot image...")
1123 script.WriteRawImage("/boot", "boot.img")
1124 else:
1125 # Produce the boot image by applying a patch to the current
1126 # contents of the boot partition, and write it back to the
1127 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001128 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001129 script.Print("Patching boot image...")
1130 script.ShowProgress(0.1, 10)
1131 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1132 % (boot_type, boot_device,
1133 source_boot.size, source_boot.sha1,
1134 target_boot.size, target_boot.sha1),
1135 "-",
1136 target_boot.size, target_boot.sha1,
1137 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001138 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001139 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001140
1141 # Do device-specific installation (eg, write radio image).
1142 device_specific.IncrementalOTA_InstallEnd()
1143
1144 if OPTIONS.extra_script is not None:
1145 script.AppendExtra(OPTIONS.extra_script)
1146
Doug Zongker922206e2014-03-04 13:16:24 -08001147 if OPTIONS.wipe_user_data:
1148 script.Print("Erasing user data...")
1149 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001150 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001151
Geremy Condra36bd3652014-02-06 19:45:10 -08001152 if OPTIONS.two_step:
1153 script.AppendExtra("""
1154set_stage("%(bcb_dev)s", "");
1155endif;
1156endif;
1157""" % bcb_dev)
1158
1159 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001160 # For downgrade OTAs, we prefer to use the update-binary in the source
1161 # build that is actually newer than the one in the target build.
1162 if OPTIONS.downgrade:
1163 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1164 else:
1165 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001166 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001167 WriteMetadata(metadata, output_zip)
1168
Doug Zongker32b527d2014-03-04 10:03:02 -08001169
Tao Bao9bc6bb22015-11-09 16:58:28 -08001170def WriteVerifyPackage(input_zip, output_zip):
1171 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1172
1173 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1174 recovery_mount_options = OPTIONS.info_dict.get(
1175 "recovery_mount_options")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001176 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -07001177 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001178 oem_dicts = _LoadOemDicts(script, oem_props, recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001179
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001180 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
1181 OPTIONS.info_dict)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001182 metadata = {
1183 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001184 "pre-device": GetOemProperty("ro.product.device", oem_props,
1185 oem_dicts and oem_dicts[0],
Tao Bao9bc6bb22015-11-09 16:58:28 -08001186 OPTIONS.info_dict),
1187 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1188 }
1189
1190 device_specific = common.DeviceSpecificParams(
1191 input_zip=input_zip,
1192 input_version=OPTIONS.info_dict["recovery_api_version"],
1193 output_zip=output_zip,
1194 script=script,
1195 input_tmp=OPTIONS.input_tmp,
1196 metadata=metadata,
1197 info_dict=OPTIONS.info_dict)
1198
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001199 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001200
1201 script.Print("Verifying device images against %s..." % target_fp)
1202 script.AppendExtra("")
1203
1204 script.Print("Verifying boot...")
1205 boot_img = common.GetBootableImage(
1206 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1207 boot_type, boot_device = common.GetTypeAndDevice(
1208 "/boot", OPTIONS.info_dict)
1209 script.Verify("%s:%s:%d:%s" % (
1210 boot_type, boot_device, boot_img.size, boot_img.sha1))
1211 script.AppendExtra("")
1212
1213 script.Print("Verifying recovery...")
1214 recovery_img = common.GetBootableImage(
1215 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1216 recovery_type, recovery_device = common.GetTypeAndDevice(
1217 "/recovery", OPTIONS.info_dict)
1218 script.Verify("%s:%s:%d:%s" % (
1219 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1220 script.AppendExtra("")
1221
Tao Bao7e0f1602017-03-06 15:50:08 -08001222 system_tgt = GetImage("system", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001223 system_tgt.ResetFileMap()
1224 system_diff = common.BlockDifference("system", system_tgt, src=None)
1225 system_diff.WriteStrictVerifyScript(script)
1226
1227 if HasVendorPartition(input_zip):
Tao Bao7e0f1602017-03-06 15:50:08 -08001228 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001229 vendor_tgt.ResetFileMap()
1230 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1231 vendor_diff.WriteStrictVerifyScript(script)
1232
1233 # Device specific partitions, such as radio, bootloader and etc.
1234 device_specific.VerifyOTA_Assertions()
1235
1236 script.SetProgress(1.0)
1237 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001238 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001239 WriteMetadata(metadata, output_zip)
1240
1241
Tao Baoc098e9e2016-01-07 13:03:56 -08001242def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1243 source_file=None):
1244 """Generate an Android OTA package that has A/B update payload."""
1245
Tao Bao2dd1c482017-02-03 16:49:39 -08001246 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1247 expected_length=None):
1248 """Compute the streaming metadata for a given zip.
1249
1250 When 'reserve_space' is True, we reserve extra space for the offset and
1251 length of the metadata entry itself, although we don't know the final
1252 values until the package gets signed. This function will be called again
1253 after signing. We then write the actual values and pad the string to the
1254 length we set earlier. Note that we can't use the actual length of the
1255 metadata entry in the second run. Otherwise the offsets for other entries
1256 will be changing again.
1257 """
Tao Baoc96316c2017-01-24 22:10:49 -08001258
1259 def ComputeEntryOffsetSize(name):
1260 """Compute the zip entry offset and size."""
1261 info = zip_file.getinfo(name)
1262 offset = info.header_offset + len(info.FileHeader())
1263 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001264 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001265
1266 # payload.bin and payload_properties.txt must exist.
1267 offsets = [ComputeEntryOffsetSize('payload.bin'),
1268 ComputeEntryOffsetSize('payload_properties.txt')]
1269
1270 # care_map.txt is available only if dm-verity is enabled.
1271 if 'care_map.txt' in zip_file.namelist():
1272 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001273
1274 # 'META-INF/com/android/metadata' is required. We don't know its actual
1275 # offset and length (as well as the values for other entries). So we
1276 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1277 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1278 # beginning of the zip), as well as the possible value changes in other
1279 # entries.
1280 if reserve_space:
1281 offsets.append('metadata:' + ' ' * 10)
1282 else:
1283 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1284
1285 value = ','.join(offsets)
1286 if expected_length is not None:
1287 assert len(value) <= expected_length, \
1288 'Insufficient reserved space: reserved=%d, actual=%d' % (
1289 expected_length, len(value))
1290 value += ' ' * (expected_length - len(value))
1291 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001292
Alex Deymod8d96ec2016-06-10 16:38:31 -07001293 # The place where the output from the subprocess should go.
1294 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1295
Tao Baoc098e9e2016-01-07 13:03:56 -08001296 # Setup signing keys.
1297 if OPTIONS.package_key is None:
1298 OPTIONS.package_key = OPTIONS.info_dict.get(
1299 "default_system_dev_certificate",
1300 "build/target/product/security/testkey")
1301
Tao Baodea0f8b2016-06-20 17:55:06 -07001302 # A/B updater expects a signing key in RSA format. Gets the key ready for
1303 # later use in step 3, unless a payload_signer has been specified.
1304 if OPTIONS.payload_signer is None:
1305 cmd = ["openssl", "pkcs8",
1306 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1307 "-inform", "DER", "-nocrypt"]
1308 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1309 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001310 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1311 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001312 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001313
Tao Baodea0f8b2016-06-20 17:55:06 -07001314 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001315 temp_zip_file = tempfile.NamedTemporaryFile()
1316 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1317 compression=zipfile.ZIP_DEFLATED)
1318
1319 # Metadata to comply with Android OTA package format.
1320 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001321 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001322 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001323 oem_dicts = _LoadOemDicts(script, None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001324
1325 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001326 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001327 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001328 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1329 OPTIONS.info_dict),
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001330 "pre-device": GetOemProperty("ro.product.device", oem_props,
1331 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001332 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001333 "ota-required-cache": "0",
1334 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001335 }
1336
1337 if source_file is not None:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001338 metadata["pre-build"] = CalculateFingerprint(oem_props,
1339 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001340 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001341 metadata["pre-build-incremental"] = GetBuildProp(
1342 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001343
Tao Baob31892e2017-02-07 11:21:17 -08001344 HandleDowngradeMetadata(metadata)
1345 else:
1346 metadata["post-timestamp"] = GetBuildProp(
1347 "ro.build.date.utc", OPTIONS.info_dict)
1348
Tao Baoc098e9e2016-01-07 13:03:56 -08001349 # 1. Generate payload.
1350 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1351 cmd = ["brillo_update_payload", "generate",
1352 "--payload", payload_file,
1353 "--target_image", target_file]
1354 if source_file is not None:
1355 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001356 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1357 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001358 assert p1.returncode == 0, "brillo_update_payload generate failed"
1359
1360 # 2. Generate hashes of the payload and metadata files.
1361 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1362 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1363 cmd = ["brillo_update_payload", "hash",
1364 "--unsigned_payload", payload_file,
1365 "--signature_size", "256",
1366 "--metadata_hash_file", metadata_sig_file,
1367 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001368 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1369 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001370 assert p1.returncode == 0, "brillo_update_payload hash failed"
1371
1372 # 3. Sign the hashes and insert them back into the payload file.
1373 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1374 suffix=".bin")
1375 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1376 suffix=".bin")
1377 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001378 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001379 cmd = [OPTIONS.payload_signer]
1380 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001381 else:
1382 cmd = ["openssl", "pkeyutl", "-sign",
1383 "-inkey", rsa_key,
1384 "-pkeyopt", "digest:sha256"]
1385 cmd.extend(["-in", payload_sig_file,
1386 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001387 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1388 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001389 assert p1.returncode == 0, "openssl sign payload failed"
1390
1391 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001392 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001393 cmd = [OPTIONS.payload_signer]
1394 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001395 else:
1396 cmd = ["openssl", "pkeyutl", "-sign",
1397 "-inkey", rsa_key,
1398 "-pkeyopt", "digest:sha256"]
1399 cmd.extend(["-in", metadata_sig_file,
1400 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001401 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1402 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001403 assert p1.returncode == 0, "openssl sign metadata failed"
1404
1405 # 3c. Insert the signatures back into the payload file.
1406 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1407 suffix=".bin")
1408 cmd = ["brillo_update_payload", "sign",
1409 "--unsigned_payload", payload_file,
1410 "--payload", signed_payload_file,
1411 "--signature_size", "256",
1412 "--metadata_signature_file", signed_metadata_sig_file,
1413 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001414 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1415 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001416 assert p1.returncode == 0, "brillo_update_payload sign failed"
1417
Alex Deymo19241c12016-02-04 22:29:29 -08001418 # 4. Dump the signed payload properties.
1419 properties_file = common.MakeTempFile(prefix="payload-properties-",
1420 suffix=".txt")
1421 cmd = ["brillo_update_payload", "properties",
1422 "--payload", signed_payload_file,
1423 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001424 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1425 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001426 assert p1.returncode == 0, "brillo_update_payload properties failed"
1427
Tao Bao7c5dc572016-06-14 17:48:11 -07001428 if OPTIONS.wipe_user_data:
1429 with open(properties_file, "a") as f:
1430 f.write("POWERWASH=1\n")
1431 metadata["ota-wipe"] = "yes"
1432
Tao Baoc96316c2017-01-24 22:10:49 -08001433 # Add the signed payload file and properties into the zip. In order to
1434 # support streaming, we pack payload.bin, payload_properties.txt and
1435 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1436 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001437 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1438 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001439 common.ZipWrite(output_zip, properties_file,
1440 arcname="payload_properties.txt",
1441 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001442
Tianjie Xucfa86222016-03-07 16:31:19 -08001443 # If dm-verity is supported for the device, copy contents of care_map
1444 # into A/B OTA package.
1445 if OPTIONS.info_dict.get("verity") == "true":
1446 target_zip = zipfile.ZipFile(target_file, "r")
1447 care_map_path = "META/care_map.txt"
1448 namelist = target_zip.namelist()
1449 if care_map_path in namelist:
1450 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001451 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1452 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001453 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001454 print("Warning: cannot find care map file in target_file package")
Tianjie Xucfa86222016-03-07 16:31:19 -08001455 common.ZipClose(target_zip)
1456
Tao Bao2dd1c482017-02-03 16:49:39 -08001457 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001458 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001459 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001460 WriteMetadata(metadata, output_zip)
1461 common.ZipClose(output_zip)
1462
Tao Bao2dd1c482017-02-03 16:49:39 -08001463 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1464 # zip entries, as well as padding the entry headers. We do a preliminary
1465 # signing (with an incomplete metadata entry) to allow that to happen. Then
1466 # compute the zip entry offsets, write back the final metadata and do the
1467 # final signing.
1468 prelim_signing = tempfile.NamedTemporaryFile()
1469 SignOutput(temp_zip_file.name, prelim_signing.name)
1470 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001471
Tao Bao2dd1c482017-02-03 16:49:39 -08001472 # Open the signed zip. Compute the final metadata that's needed for streaming.
1473 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1474 compression=zipfile.ZIP_DEFLATED)
1475 expected_length = len(metadata['ota-streaming-property-files'])
1476 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1477 prelim_zip, reserve_space=False, expected_length=expected_length)
1478
1479 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1480 final_signing = tempfile.NamedTemporaryFile()
1481 output_zip = zipfile.ZipFile(final_signing, "w",
1482 compression=zipfile.ZIP_DEFLATED)
1483 for item in prelim_zip.infolist():
1484 if item.filename == METADATA_NAME:
1485 continue
1486
1487 data = prelim_zip.read(item.filename)
1488 out_info = copy.copy(item)
1489 common.ZipWriteStr(output_zip, out_info, data)
1490
1491 # Now write the final metadata entry.
1492 WriteMetadata(metadata, output_zip)
1493 common.ZipClose(prelim_zip)
1494 common.ZipClose(output_zip)
1495
1496 # Re-sign the package after updating the metadata entry.
1497 SignOutput(final_signing.name, output_file)
1498 final_signing.close()
1499
1500 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001501 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001502 actual = metadata['ota-streaming-property-files'].strip()
1503 expected = ComputeStreamingMetadata(output_zip)
1504 assert actual == expected, \
1505 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001506 common.ZipClose(output_zip)
1507
Tao Baoc098e9e2016-01-07 13:03:56 -08001508
Dan Albert8b72aef2015-03-23 19:13:21 -07001509class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001510 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001511 self.deferred_patch_list = None
Tao Bao89fbb0f2017-01-10 10:47:58 -08001512 print("Loading target...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001513 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001514 print("Loading source...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001515 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1516
1517 self.verbatim_targets = verbatim_targets = []
1518 self.patch_list = patch_list = []
1519 diffs = []
1520 self.renames = renames = {}
1521 known_paths = set()
1522 largest_source_size = 0
1523
1524 matching_file_cache = {}
1525 for fn, sf in source_data.items():
1526 assert fn == sf.name
1527 matching_file_cache["path:" + fn] = sf
1528 if fn in target_data.keys():
1529 AddToKnownPaths(fn, known_paths)
1530 # Only allow eligibility for filename/sha matching
1531 # if there isn't a perfect path match.
1532 if target_data.get(sf.name) is None:
1533 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1534 matching_file_cache["sha:" + sf.sha1] = sf
1535
1536 for fn in sorted(target_data.keys()):
1537 tf = target_data[fn]
1538 assert fn == tf.name
1539 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1540 if sf is not None and sf.name != tf.name:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001541 print("File has moved from " + sf.name + " to " + tf.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001542 renames[sf.name] = tf
1543
1544 if sf is None or fn in OPTIONS.require_verbatim:
1545 # This file should be included verbatim
1546 if fn in OPTIONS.prohibit_verbatim:
1547 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Tao Bao89fbb0f2017-01-10 10:47:58 -08001548 print("send", fn, "verbatim")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001549 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001550 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001551 if fn in target_data.keys():
1552 AddToKnownPaths(fn, known_paths)
1553 elif tf.sha1 != sf.sha1:
1554 # File is different; consider sending as a patch
1555 diffs.append(common.Difference(tf, sf))
1556 else:
1557 # Target file data identical to source (may still be renamed)
1558 pass
1559
1560 common.ComputeDifferences(diffs)
1561
1562 for diff in diffs:
1563 tf, sf, d = diff.GetPatch()
1564 path = "/".join(tf.name.split("/")[:-1])
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001565 if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001566 path not in known_paths:
1567 # patch is almost as big as the file; don't bother patching
1568 # or a patch + rename cannot take place due to the target
1569 # directory not existing
1570 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001571 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001572 if sf.name in renames:
1573 del renames[sf.name]
1574 AddToKnownPaths(tf.name, known_paths)
1575 else:
1576 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1577 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1578 largest_source_size = max(largest_source_size, sf.size)
1579
1580 self.largest_source_size = largest_source_size
1581
1582 def EmitVerification(self, script):
1583 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001584 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001585 if tf.name != sf.name:
1586 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1587 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1588 so_far += sf.size
1589 return so_far
1590
Michael Runge63f01de2014-10-28 19:24:19 -07001591 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001592 for fn, _, sha1 in self.verbatim_targets:
1593 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001594 script.FileCheck("/"+fn, sha1)
1595 for tf, _, _, _ in self.patch_list:
1596 script.FileCheck(tf.name, tf.sha1)
1597
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001598 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001599 file_list = ["/" + i[0] for i in self.verbatim_targets]
1600 file_list += ["/" + i for i in self.source_data
1601 if i not in self.target_data and i not in self.renames]
1602 file_list += list(extras)
1603 # Sort the list in descending order, which removes all the files first
1604 # before attempting to remove the folder. (Bug: 22960996)
1605 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001606
1607 def TotalPatchSize(self):
1608 return sum(i[1].size for i in self.patch_list)
1609
1610 def EmitPatches(self, script, total_patch_size, so_far):
1611 self.deferred_patch_list = deferred_patch_list = []
1612 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001613 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001614 if tf.name == "system/build.prop":
1615 deferred_patch_list.append(item)
1616 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001617 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001618 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001619 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1620 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001621 so_far += tf.size
1622 script.SetProgress(so_far / total_patch_size)
1623 return so_far
1624
1625 def EmitDeferredPatches(self, script):
1626 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001627 tf, sf, _, _ = item
1628 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1629 "patch/" + sf.name + ".p")
1630 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001631
1632 def EmitRenames(self, script):
1633 if len(self.renames) > 0:
1634 script.Print("Renaming files...")
1635 for src, tgt in self.renames.iteritems():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001636 print("Renaming " + src + " to " + tgt.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001637 script.RenameFile(src, tgt.name)
1638
1639
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001640def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001641 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1642 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1643
Doug Zongker26e66192014-02-20 13:22:07 -08001644 if (OPTIONS.block_based and
1645 target_has_recovery_patch and
1646 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001647 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1648
Doug Zongker37974732010-09-16 17:44:38 -07001649 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1650 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001651
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001652 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001653 print("WARNING: generating edify script for a source that "
1654 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001655 script = edify_generator.EdifyGenerator(
1656 source_version, OPTIONS.target_info_dict,
1657 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001658
Tao Bao34b47bf2015-06-22 19:17:41 -07001659 recovery_mount_options = OPTIONS.source_info_dict.get(
1660 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001661 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1662 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001663 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -07001664 if source_oem_props or target_oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001665 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -07001666
Dan Albert8b72aef2015-03-23 19:13:21 -07001667 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001668 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001669 oem_dicts and oem_dicts[0],
1670 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001671 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001672 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001673
Tao Baob31892e2017-02-07 11:21:17 -08001674 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -08001675
Doug Zongker05d3dea2009-06-22 11:32:31 -07001676 device_specific = common.DeviceSpecificParams(
1677 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001678 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001679 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001680 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001681 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001682 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001683 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001684 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001685
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001686 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001687 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001688 if HasVendorPartition(target_zip):
1689 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001690 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001691 else:
1692 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001693
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001694 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001695 OPTIONS.target_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001696 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001697 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001698
Tao Bao3e30d972016-03-15 13:20:19 -07001699 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001700 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001701 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001702 script.AssertSomeThumbprint(
1703 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1704 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001705 elif source_oem_props is None and target_oem_props is not None:
1706 script.AssertFingerprintOrThumbprint(
1707 source_fp,
1708 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1709 else:
1710 script.AssertFingerprintOrThumbprint(
1711 target_fp,
1712 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001713
Doug Zongker2ea21062010-04-28 16:05:21 -07001714 metadata["pre-build"] = source_fp
1715 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001716 metadata["pre-build-incremental"] = GetBuildProp(
1717 "ro.build.version.incremental", OPTIONS.source_info_dict)
1718 metadata["post-build-incremental"] = GetBuildProp(
1719 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001720
Doug Zongker55d93282011-01-25 17:03:34 -08001721 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001722 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1723 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001724 target_boot = common.GetBootableImage(
1725 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001726 updating_boot = (not OPTIONS.two_step and
1727 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001728
Doug Zongker55d93282011-01-25 17:03:34 -08001729 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001730 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1731 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001732 target_recovery = common.GetBootableImage(
1733 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001734 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001735
Doug Zongker881dd402009-09-20 14:03:55 -07001736 # Here's how we divide up the progress bar:
1737 # 0.1 for verifying the start state (PatchCheck calls)
1738 # 0.8 for applying patches (ApplyPatch calls)
1739 # 0.1 for unpacking verbatim files, symlinking, and doing the
1740 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001741
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001742 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001743 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001744
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001745 # Two-step incremental package strategy (in chronological order,
1746 # which is *not* the order in which the generated script has
1747 # things):
1748 #
1749 # if stage is not "2/3" or "3/3":
1750 # do verification on current system
1751 # write recovery image to boot partition
1752 # set stage to "2/3"
1753 # reboot to boot partition and restart recovery
1754 # else if stage is "2/3":
1755 # write recovery image to recovery partition
1756 # set stage to "3/3"
1757 # reboot to recovery partition and restart recovery
1758 # else:
1759 # (stage must be "3/3")
1760 # perform update:
1761 # patch system files, etc.
1762 # force full install of new boot image
1763 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001764 # complete script normally
1765 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001766
1767 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001768 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001769 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001770 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001771 assert fs.fs_type.upper() == "EMMC", \
1772 "two-step packages only supported on devices with EMMC /misc partitions"
1773 bcb_dev = {"bcb_dev": fs.device}
1774 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1775 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001776if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001777""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001778
1779 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1780 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001781 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001782 script.WriteRawImage("/recovery", "recovery.img")
1783 script.AppendExtra("""
1784set_stage("%(bcb_dev)s", "3/3");
1785reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001786else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001787""" % bcb_dev)
1788
Tao Baod42e97e2016-11-30 12:11:57 -08001789 # Stage 1/3: (a) Verify the current system.
1790 script.Comment("Stage 1/3")
1791
Tao Bao6c55a8a2015-04-08 15:30:27 -07001792 # Dump fingerprints
1793 script.Print("Source: %s" % (source_fp,))
1794 script.Print("Target: %s" % (target_fp,))
1795
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001796 script.Print("Verifying current system...")
1797
Doug Zongkere5ff5902012-01-17 10:55:37 -08001798 device_specific.IncrementalOTA_VerifyBegin()
1799
Doug Zongker881dd402009-09-20 14:03:55 -07001800 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001801 so_far = system_diff.EmitVerification(script)
1802 if vendor_diff:
1803 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001804
Tao Baod8d14be2016-02-04 14:26:02 -08001805 size = []
1806 if system_diff.patch_list:
1807 size.append(system_diff.largest_source_size)
1808 if vendor_diff:
1809 if vendor_diff.patch_list:
1810 size.append(vendor_diff.largest_source_size)
1811
Doug Zongker5da317e2009-06-02 13:38:17 -07001812 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001813 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001814 _, _, d = d.ComputePatch()
Tao Bao89fbb0f2017-01-10 10:47:58 -08001815 print("boot target: %d source: %d diff: %d" % (
1816 target_boot.size, source_boot.size, len(d)))
Doug Zongker5da317e2009-06-02 13:38:17 -07001817
Doug Zongker048e7ca2009-06-15 14:31:53 -07001818 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001819
Tao Baodd24da92015-07-29 14:09:23 -07001820 boot_type, boot_device = common.GetTypeAndDevice(
1821 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001822
1823 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1824 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001825 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001826 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001827 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001828 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001829
Tao Baod8d14be2016-02-04 14:26:02 -08001830 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001831 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001832
Doug Zongker05d3dea2009-06-22 11:32:31 -07001833 device_specific.IncrementalOTA_VerifyEnd()
1834
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001835 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001836 # Stage 1/3: (b) Write recovery image to /boot.
1837 _WriteRecoveryImageToBoot(script, output_zip)
1838
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001839 script.AppendExtra("""
1840set_stage("%(bcb_dev)s", "2/3");
1841reboot_now("%(bcb_dev)s", "");
1842else
1843""" % bcb_dev)
1844
Tao Baod42e97e2016-11-30 12:11:57 -08001845 # Stage 3/3: Make changes.
1846 script.Comment("Stage 3/3")
1847
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001848 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001849
Doug Zongkere5ff5902012-01-17 10:55:37 -08001850 device_specific.IncrementalOTA_InstallBegin()
1851
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001852 if OPTIONS.two_step:
1853 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1854 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001855 print("writing full boot image (forced by two-step mode)")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001856
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001857 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001858 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1859 if vendor_diff:
1860 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001861
Doug Zongker881dd402009-09-20 14:03:55 -07001862 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001863 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1864 if vendor_diff:
1865 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001866 if updating_boot:
1867 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001868
1869 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001870 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1871 if vendor_diff:
1872 script.Print("Patching vendor files...")
1873 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001874
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001875 if not OPTIONS.two_step:
1876 if updating_boot:
1877 # Produce the boot image by applying a patch to the current
1878 # contents of the boot partition, and write it back to the
1879 # partition.
1880 script.Print("Patching boot image...")
1881 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1882 % (boot_type, boot_device,
1883 source_boot.size, source_boot.sha1,
1884 target_boot.size, target_boot.sha1),
1885 "-",
1886 target_boot.size, target_boot.sha1,
1887 source_boot.sha1, "patch/boot.img.p")
1888 so_far += target_boot.size
1889 script.SetProgress(so_far / total_patch_size)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001890 print("boot image changed; including.")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001891 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001892 print("boot image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001893
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001894 system_items = ItemSet("system", "META/filesystem_config.txt")
1895 if vendor_diff:
1896 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1897
Doug Zongkereef39442009-04-02 12:14:19 -07001898 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001899 # Recovery is generated as a patch using both the boot image
1900 # (which contains the same linux kernel as recovery) and the file
1901 # /system/etc/recovery-resource.dat (which contains all the images
1902 # used in the recovery UI) as sources. This lets us minimize the
1903 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001904 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001905 # For older builds where recovery-resource.dat is not present, we
1906 # use only the boot image as the source.
1907
Doug Zongkerc9253822014-02-04 12:17:58 -08001908 if not target_has_recovery_patch:
1909 def output_sink(fn, data):
1910 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001911 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001912
1913 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1914 target_recovery, target_boot)
1915 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001916 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001917 "/system/etc/install-recovery.sh"])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001918 print("recovery image changed; including as patch from boot.")
Doug Zongkereef39442009-04-02 12:14:19 -07001919 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001920 print("recovery image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001921
Doug Zongker881dd402009-09-20 14:03:55 -07001922 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001923
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001924 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1925 if vendor_diff:
1926 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1927
1928 temp_script = script.MakeTemporary()
1929 system_items.GetMetadata(target_zip)
1930 system_items.Get("system").SetPermissions(temp_script)
1931 if vendor_diff:
1932 vendor_items.GetMetadata(target_zip)
1933 vendor_items.Get("vendor").SetPermissions(temp_script)
1934
1935 # Note that this call will mess up the trees of Items, so make sure
1936 # we're done with them.
1937 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1938 if vendor_diff:
1939 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001940
1941 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001942 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1943
1944 # Delete all the symlinks in source that aren't in target. This
1945 # needs to happen before verbatim files are unpacked, in case a
1946 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001947
1948 # If a symlink in the source will be replaced by a regular file, we cannot
1949 # delete the symlink/file in case the package gets applied again. For such
1950 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1951 # (Bug: 23646151)
1952 replaced_symlinks = dict()
1953 if system_diff:
1954 for i in system_diff.verbatim_targets:
1955 replaced_symlinks["/%s" % (i[0],)] = i[2]
1956 if vendor_diff:
1957 for i in vendor_diff.verbatim_targets:
1958 replaced_symlinks["/%s" % (i[0],)] = i[2]
1959
1960 if system_diff:
1961 for tf in system_diff.renames.values():
1962 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1963 if vendor_diff:
1964 for tf in vendor_diff.renames.values():
1965 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1966
1967 always_delete = []
1968 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001969 for dest, link in source_symlinks:
1970 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001971 if link in replaced_symlinks:
1972 may_delete.append((link, replaced_symlinks[link]))
1973 else:
1974 always_delete.append(link)
1975 script.DeleteFiles(always_delete)
1976 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001977
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001978 if system_diff.verbatim_targets:
1979 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001980 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001981 if vendor_diff and vendor_diff.verbatim_targets:
1982 script.Print("Unpacking new vendor files...")
1983 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001984
Doug Zongkerc9253822014-02-04 12:17:58 -08001985 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001986 script.Print("Unpacking new recovery...")
1987 script.UnpackPackageDir("recovery", "/system")
1988
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001989 system_diff.EmitRenames(script)
1990 if vendor_diff:
1991 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001992
Doug Zongker05d3dea2009-06-22 11:32:31 -07001993 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001994
1995 # Create all the symlinks that don't already exist, or point to
1996 # somewhere different than what we want. Delete each symlink before
1997 # creating it, since the 'symlink' command won't overwrite.
1998 to_create = []
1999 for dest, link in target_symlinks:
2000 if link in source_symlinks_d:
2001 if dest != source_symlinks_d[link]:
2002 to_create.append((dest, link))
2003 else:
2004 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002005 script.DeleteFiles([i[1] for i in to_create])
2006 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07002007
2008 # Now that the symlinks are created, we can set all the
2009 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002010 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07002011
Doug Zongker881dd402009-09-20 14:03:55 -07002012 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07002013 device_specific.IncrementalOTA_InstallEnd()
2014
Doug Zongker1c390a22009-05-14 19:06:36 -07002015 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07002016 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07002017
Doug Zongkere92f15a2011-08-26 13:46:40 -07002018 # Patch the build.prop file last, so if something fails but the
2019 # device can still come up, it appears to be the old build and will
2020 # get set the OTA package again to retry.
2021 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07002022 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07002023
Doug Zongker922206e2014-03-04 13:16:24 -08002024 if OPTIONS.wipe_user_data:
2025 script.Print("Erasing user data...")
2026 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08002027 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08002028
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002029 if OPTIONS.two_step:
2030 script.AppendExtra("""
2031set_stage("%(bcb_dev)s", "");
2032endif;
2033endif;
2034""" % bcb_dev)
2035
Michael Runge63f01de2014-10-28 19:24:19 -07002036 if OPTIONS.verify and system_diff:
2037 script.Print("Remounting and verifying system partition files...")
2038 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08002039 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002040 system_diff.EmitExplicitTargetVerification(script)
2041
2042 if OPTIONS.verify and vendor_diff:
2043 script.Print("Remounting and verifying vendor partition files...")
2044 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08002045 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002046 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08002047
2048 # For downgrade OTAs, we prefer to use the update-binary in the source
2049 # build that is actually newer than the one in the target build.
2050 if OPTIONS.downgrade:
2051 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
2052 else:
2053 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07002054
Tao Baod8d14be2016-02-04 14:26:02 -08002055 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07002056 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07002057
2058
2059def main(argv):
2060
2061 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08002062 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002063 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07002064 elif o in ("-k", "--package_key"):
2065 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002066 elif o in ("-i", "--incremental_from"):
2067 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002068 elif o == "--full_radio":
2069 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002070 elif o == "--full_bootloader":
2071 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002072 elif o in ("-w", "--wipe_user_data"):
2073 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002074 elif o == "--downgrade":
2075 OPTIONS.downgrade = True
2076 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002077 elif o == "--override_timestamp":
2078 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07002079 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002080 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002081 elif o == "--oem_no_mount":
2082 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002083 elif o in ("-e", "--extra_script"):
2084 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002085 elif o in ("-t", "--worker_threads"):
2086 if a.isdigit():
2087 OPTIONS.worker_threads = int(a)
2088 else:
2089 raise ValueError("Cannot parse value %r for option %r - only "
2090 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002091 elif o in ("-2", "--two_step"):
2092 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002093 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002094 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002095 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002096 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002097 elif o == "--block":
2098 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002099 elif o in ("-b", "--binary"):
2100 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002101 elif o in ("--no_fallback_to_full",):
2102 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002103 elif o == "--stash_threshold":
2104 try:
2105 OPTIONS.stash_threshold = float(a)
2106 except ValueError:
2107 raise ValueError("Cannot parse value %r for option %r - expecting "
2108 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002109 elif o == "--gen_verify":
2110 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002111 elif o == "--log_diff":
2112 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002113 elif o == "--payload_signer":
2114 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002115 elif o == "--payload_signer_args":
2116 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002117 else:
2118 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002119 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002120
2121 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08002122 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002123 extra_long_opts=[
2124 "board_config=",
2125 "package_key=",
2126 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002127 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002128 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002129 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002130 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002131 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002132 "extra_script=",
2133 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002134 "two_step",
2135 "no_signing",
2136 "block",
2137 "binary=",
2138 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002139 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002140 "verify",
2141 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002142 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002143 "gen_verify",
2144 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002145 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002146 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002147 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002148
2149 if len(args) != 2:
2150 common.Usage(__doc__)
2151 sys.exit(1)
2152
Tao Bao5d182562016-02-23 11:38:39 -08002153 if OPTIONS.downgrade:
2154 # Sanity check to enforce a data wipe.
2155 if not OPTIONS.wipe_user_data:
2156 raise ValueError("Cannot downgrade without a data wipe")
2157
2158 # We should only allow downgrading incrementals (as opposed to full).
2159 # Otherwise the device may go back from arbitrary build with this full
2160 # OTA package.
2161 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002162 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002163
Tao Bao3e6161a2017-02-28 11:48:48 -08002164 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
2165 "Cannot have --downgrade AND --override_timestamp both"
2166
Tao Baoc098e9e2016-01-07 13:03:56 -08002167 # Load the dict file from the zip directly to have a peek at the OTA type.
2168 # For packages using A/B update, unzipping is not needed.
2169 input_zip = zipfile.ZipFile(args[0], "r")
2170 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2171 common.ZipClose(input_zip)
2172
2173 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2174
2175 if ab_update:
2176 if OPTIONS.incremental_source is not None:
2177 OPTIONS.target_info_dict = OPTIONS.info_dict
2178 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2179 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2180 common.ZipClose(source_zip)
2181
2182 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002183 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002184 common.DumpInfoDict(OPTIONS.info_dict)
2185
2186 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002187 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002188 common.DumpInfoDict(OPTIONS.source_info_dict)
2189
2190 WriteABOTAPackageWithBrilloScript(
2191 target_file=args[0],
2192 output_file=args[1],
2193 source_file=OPTIONS.incremental_source)
2194
Tao Bao89fbb0f2017-01-10 10:47:58 -08002195 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002196 return
2197
Doug Zongker1c390a22009-05-14 19:06:36 -07002198 if OPTIONS.extra_script is not None:
2199 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2200
Tao Bao89fbb0f2017-01-10 10:47:58 -08002201 print("unzipping target target-files...")
Tao Bao6b0b2f92017-03-05 11:38:11 -08002202 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
2203 args[0], UNZIP_PATTERN if OPTIONS.block_based else None)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002204
Doug Zongkereef39442009-04-02 12:14:19 -07002205 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002206 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002207
Doug Zongker37974732010-09-16 17:44:38 -07002208 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002209 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07002210 common.DumpInfoDict(OPTIONS.info_dict)
2211
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002212 # If the caller explicitly specified the device-specific extensions
2213 # path via -s/--device_specific, use that. Otherwise, use
2214 # META/releasetools.py if it is present in the target target_files.
2215 # Otherwise, take the path of the file from 'tool_extensions' in the
2216 # info dict and look for that in the local filesystem, relative to
2217 # the current directory.
2218
Doug Zongker37974732010-09-16 17:44:38 -07002219 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002220 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2221 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08002222 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002223 OPTIONS.device_specific = from_input
2224 else:
2225 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2226
Doug Zongker37974732010-09-16 17:44:38 -07002227 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002228 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002229
Tao Baoc098e9e2016-01-07 13:03:56 -08002230 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002231 raise common.ExternalError(
2232 "--- target build has specified no recovery ---")
2233
Tao Bao767e3ac2015-11-10 12:19:19 -08002234 # Use the default key to sign the package if not specified with package_key.
2235 if not OPTIONS.no_signing:
2236 if OPTIONS.package_key is None:
2237 OPTIONS.package_key = OPTIONS.info_dict.get(
2238 "default_system_dev_certificate",
2239 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002240
Tao Bao767e3ac2015-11-10 12:19:19 -08002241 # Set up the output zip. Create a temporary zip file if signing is needed.
2242 if OPTIONS.no_signing:
2243 if os.path.exists(args[1]):
2244 os.unlink(args[1])
2245 output_zip = zipfile.ZipFile(args[1], "w",
2246 compression=zipfile.ZIP_DEFLATED)
2247 else:
2248 temp_zip_file = tempfile.NamedTemporaryFile()
2249 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2250 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002251
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002252 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002253 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002254 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002255 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002256 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002257
Tao Bao9bc6bb22015-11-09 16:58:28 -08002258 # Generate a verify package.
2259 if OPTIONS.gen_verify:
2260 WriteVerifyPackage(input_zip, output_zip)
2261
Tao Bao767e3ac2015-11-10 12:19:19 -08002262 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002263 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002264 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002265
2266 # Generate an incremental OTA. It will fall back to generate a full OTA on
2267 # failure unless no_fallback_to_full is specified.
2268 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002269 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08002270 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08002271 OPTIONS.incremental_source,
2272 UNZIP_PATTERN if OPTIONS.block_based else None)
Tao Bao767e3ac2015-11-10 12:19:19 -08002273 OPTIONS.target_info_dict = OPTIONS.info_dict
2274 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2275 OPTIONS.source_tmp)
2276 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002277 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002278 common.DumpInfoDict(OPTIONS.source_info_dict)
2279 try:
2280 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002281 if OPTIONS.log_diff:
2282 out_file = open(OPTIONS.log_diff, 'w')
2283 import target_files_diff
2284 target_files_diff.recursiveDiff('',
2285 OPTIONS.source_tmp,
2286 OPTIONS.input_tmp,
2287 out_file)
2288 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002289 except ValueError:
2290 if not OPTIONS.fallback_to_full:
2291 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08002292 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002293 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002294 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002295
Tao Bao767e3ac2015-11-10 12:19:19 -08002296 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002297
Tao Bao767e3ac2015-11-10 12:19:19 -08002298 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002299 if not OPTIONS.no_signing:
2300 SignOutput(temp_zip_file.name, args[1])
2301 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002302
Tao Bao89fbb0f2017-01-10 10:47:58 -08002303 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002304
2305
2306if __name__ == '__main__':
2307 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002308 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002309 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002310 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002311 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002312 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002313 finally:
2314 common.Cleanup()