blob: be01a6d3412c00869d83f439fa0f55ee2c363420 [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'
187
Doug Zongkereef39442009-04-02 12:14:19 -0700188def MostPopularKey(d, default):
189 """Given a dict, return the key corresponding to the largest
190 value. Returns 'default' if the dict is empty."""
191 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700192 if not x:
193 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700194 x.sort()
195 return x[-1][1]
196
197
198def IsSymlink(info):
199 """Return true if the zipfile.ZipInfo object passed in represents a
200 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700201 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700202
Hristo Bojinov96be7202010-08-02 10:26:17 -0700203def IsRegular(info):
204 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700205 regular file."""
206 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700207
Michael Runge4038aa82013-12-13 18:06:28 -0800208def ClosestFileMatch(src, tgtfiles, existing):
209 """Returns the closest file match between a source file and list
210 of potential matches. The exact filename match is preferred,
211 then the sha1 is searched for, and finally a file with the same
212 basename is evaluated. Rename support in the updater-binary is
213 required for the latter checks to be used."""
214
215 result = tgtfiles.get("path:" + src.name)
216 if result is not None:
217 return result
218
219 if not OPTIONS.target_info_dict.get("update_rename_support", False):
220 return None
221
222 if src.size < 1000:
223 return None
224
225 result = tgtfiles.get("sha1:" + src.sha1)
226 if result is not None and existing.get(result.name) is None:
227 return result
228 result = tgtfiles.get("file:" + src.name.split("/")[-1])
229 if result is not None and existing.get(result.name) is None:
230 return result
231 return None
232
Dan Albert8b72aef2015-03-23 19:13:21 -0700233class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700234 def __init__(self, partition, fs_config):
235 self.partition = partition
236 self.fs_config = fs_config
237 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700238
Dan Albert8b72aef2015-03-23 19:13:21 -0700239 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700240 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700241 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700242 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700243
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700244 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700245 # The target_files contains a record of what the uid,
246 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700247 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700248
249 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700250 if not line:
251 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700252 columns = line.split()
253 name, uid, gid, mode = columns[:4]
254 selabel = None
255 capabilities = None
256
257 # After the first 4 columns, there are a series of key=value
258 # pairs. Extract out the fields we care about.
259 for element in columns[4:]:
260 key, value = element.split("=")
261 if key == "selabel":
262 selabel = value
263 if key == "capabilities":
264 capabilities = value
265
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700266 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700267 if i is not None:
268 i.uid = int(uid)
269 i.gid = int(gid)
270 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700271 i.selabel = selabel
272 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700273 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700274 i.children.sort(key=lambda i: i.name)
275
Tao Baof2cffbd2015-07-22 12:33:18 -0700276 # Set metadata for the files generated by this script. For full recovery
277 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700278 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700279 if i:
280 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700281 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700282 if i:
283 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700284
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700285
Dan Albert8b72aef2015-03-23 19:13:21 -0700286class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700287 """Items represent the metadata (user, group, mode) of files and
288 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700289 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700290 self.itemset = itemset
291 self.name = name
292 self.uid = None
293 self.gid = None
294 self.mode = None
295 self.selabel = None
296 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700297 self.is_dir = is_dir
298 self.descendants = None
299 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700300
301 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700302 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700303 self.parent.children.append(self)
304 else:
305 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700306 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700307 self.children = []
308
309 def Dump(self, indent=0):
310 if self.uid is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800311 print("%s%s %d %d %o" % (
312 " " * indent, self.name, self.uid, self.gid, self.mode))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700313 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800314 print("%s%s %s %s %s" % (
315 " " * indent, self.name, self.uid, self.gid, self.mode))
Dan Albert8b72aef2015-03-23 19:13:21 -0700316 if self.is_dir:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800317 print("%s%s" % (" " * indent, self.descendants))
318 print("%s%s" % (" " * indent, self.best_subtree))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700319 for i in self.children:
320 i.Dump(indent=indent+1)
321
Doug Zongkereef39442009-04-02 12:14:19 -0700322 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700323 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700324 all children and determine the best strategy for using set_perm_recursive
325 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700326 values. Recursively calls itself for all descendants.
327
Dan Albert8b72aef2015-03-23 19:13:21 -0700328 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
329 counting up all descendants of this node. (dmode or fmode may be None.)
330 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
331 fmode, selabel, capabilities) tuple that will match the most descendants of
332 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700333 """
334
Dan Albert8b72aef2015-03-23 19:13:21 -0700335 assert self.is_dir
336 key = (self.uid, self.gid, self.mode, None, self.selabel,
337 self.capabilities)
338 self.descendants = {key: 1}
339 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700340 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700341 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700342 for k, v in i.CountChildMetadata().iteritems():
343 d[k] = d.get(k, 0) + v
344 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700345 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700346 d[k] = d.get(k, 0) + 1
347
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700348 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
349 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700350
351 # First, find the (uid, gid) pair that matches the most
352 # descendants.
353 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700354 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700355 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
356 ug = MostPopularKey(ug, (0, 0))
357
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700358 # Now find the dmode, fmode, selabel, and capabilities that match
359 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700360 best_dmode = (0, 0o755)
361 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700362 best_selabel = (0, None)
363 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700364 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700365 if k[:2] != ug:
366 continue
367 if k[2] is not None and count >= best_dmode[0]:
368 best_dmode = (count, k[2])
369 if k[3] is not None and count >= best_fmode[0]:
370 best_fmode = (count, k[3])
371 if k[4] is not None and count >= best_selabel[0]:
372 best_selabel = (count, k[4])
373 if k[5] is not None and count >= best_capabilities[0]:
374 best_capabilities = (count, k[5])
375 self.best_subtree = ug + (
376 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700377
378 return d
379
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700380 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700381 """Append set_perm/set_perm_recursive commands to 'script' to
382 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700383 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700384
385 self.CountChildMetadata()
386
387 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700388 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
389 # that the current item (and all its children) have already been set to.
390 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700391 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700392 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700393 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700394 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700395 current = item.best_subtree
396
397 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700398 item.mode != current[2] or item.selabel != current[4] or \
399 item.capabilities != current[5]:
400 script.SetPermissions("/"+item.name, item.uid, item.gid,
401 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700402
403 for i in item.children:
404 recurse(i, current)
405 else:
406 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700407 item.mode != current[3] or item.selabel != current[4] or \
408 item.capabilities != current[5]:
409 script.SetPermissions("/"+item.name, item.uid, item.gid,
410 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700411
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700412 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700413
414
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700415def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
416 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700417 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800418 list of symlinks. output_zip may be None, in which case the copy is
419 skipped (but the other side effects still happen). substitute is an
420 optional dict of {output filename: contents} to be output instead of
421 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700422 """
423
424 symlinks = []
425
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700426 partition = itemset.partition
427
Doug Zongkereef39442009-04-02 12:14:19 -0700428 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700429 prefix = partition.upper() + "/"
430 if info.filename.startswith(prefix):
431 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700432 if IsSymlink(info):
433 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700434 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700435 else:
436 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700437 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700438 if substitute and fn in substitute and substitute[fn] is None:
439 continue
440 if output_zip is not None:
441 if substitute and fn in substitute:
442 data = substitute[fn]
443 else:
444 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700445 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700446 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700447 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700448 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700449 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700450
451 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800452 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700453
454
Doug Zongkereef39442009-04-02 12:14:19 -0700455def SignOutput(temp_zip_name, output_zip_name):
456 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
457 pw = key_passwords[OPTIONS.package_key]
458
Doug Zongker951495f2009-08-14 12:44:19 -0700459 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
460 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700461
462
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800463def AppendAssertions(script, info_dict, oem_dicts=None):
Michael Runge6e836112014-04-15 17:40:21 -0700464 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700465 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700466 device = GetBuildProp("ro.product.device", info_dict)
467 script.AssertDevice(device)
468 else:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800469 if not oem_dicts:
Dan Albert8b72aef2015-03-23 19:13:21 -0700470 raise common.ExternalError(
471 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700472 for prop in oem_props.split():
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800473 values = []
474 for oem_dict in oem_dicts:
475 if oem_dict.get(prop):
476 values.append(oem_dict[prop])
477 if not values:
Dan Albert8b72aef2015-03-23 19:13:21 -0700478 raise common.ExternalError(
479 "The OEM file is missing the property %s" % prop)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800480 script.AssertOemProperty(prop, values)
481
482
483def _LoadOemDicts(script, recovery_mount_options):
484 """Returns the list of loaded OEM properties dict."""
485 oem_dicts = None
486 if OPTIONS.oem_source is None:
487 raise common.ExternalError("OEM source required for this build")
488 if not OPTIONS.oem_no_mount:
489 script.Mount("/oem", recovery_mount_options)
490 oem_dicts = []
491 for oem_file in OPTIONS.oem_source:
492 oem_dicts.append(common.LoadDictionaryFromLines(
493 open(oem_file).readlines()))
494 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700495
Doug Zongkereef39442009-04-02 12:14:19 -0700496
Tao Baod42e97e2016-11-30 12:11:57 -0800497def _WriteRecoveryImageToBoot(script, output_zip):
498 """Find and write recovery image to /boot in two-step OTA.
499
500 In two-step OTAs, we write recovery image to /boot as the first step so that
501 we can reboot to there and install a new recovery image to /recovery.
502 A special "recovery-two-step.img" will be preferred, which encodes the correct
503 path of "/boot". Otherwise the device may show "device is corrupt" message
504 when booting into /boot.
505
506 Fall back to using the regular recovery.img if the two-step recovery image
507 doesn't exist. Note that rebuilding the special image at this point may be
508 infeasible, because we don't have the desired boot signer and keys when
509 calling ota_from_target_files.py.
510 """
511
512 recovery_two_step_img_name = "recovery-two-step.img"
513 recovery_two_step_img_path = os.path.join(
514 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
515 if os.path.exists(recovery_two_step_img_path):
516 recovery_two_step_img = common.GetBootableImage(
517 recovery_two_step_img_name, recovery_two_step_img_name,
518 OPTIONS.input_tmp, "RECOVERY")
519 common.ZipWriteStr(
520 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800521 print("two-step package: using %s in stage 1/3" % (
522 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800523 script.WriteRawImage("/boot", recovery_two_step_img_name)
524 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800525 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800526 # The "recovery.img" entry has been written into package earlier.
527 script.WriteRawImage("/boot", "recovery.img")
528
529
Doug Zongkerc9253822014-02-04 12:17:58 -0800530def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700531 namelist = [name for name in target_files_zip.namelist()]
532 return ("SYSTEM/recovery-from-boot.p" in namelist or
533 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700534
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700535def HasVendorPartition(target_files_zip):
536 try:
537 target_files_zip.getinfo("VENDOR/")
538 return True
539 except KeyError:
540 return False
541
Michael Runge6e836112014-04-15 17:40:21 -0700542def GetOemProperty(name, oem_props, oem_dict, info_dict):
543 if oem_props is not None and name in oem_props:
544 return oem_dict[name]
545 return GetBuildProp(name, info_dict)
546
547
548def CalculateFingerprint(oem_props, oem_dict, info_dict):
549 if oem_props is None:
550 return GetBuildProp("ro.build.fingerprint", info_dict)
551 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700552 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
553 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
554 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
555 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700556
Doug Zongkerfc44a512014-08-26 13:10:25 -0700557
Doug Zongker3c84f562014-07-31 11:06:30 -0700558def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700559 # Return an image object (suitable for passing to BlockImageDiff)
560 # for the 'which' partition (most be "system" or "vendor"). If a
561 # prebuilt image and file map are found in tmpdir they are used,
562 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700563
564 assert which in ("system", "vendor")
565
566 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700567 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
568 if os.path.exists(path) and os.path.exists(mappath):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800569 print("using %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700570 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700571
572 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800573 print("building %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700574
575 # This is an 'old' target-files, which does not contain images
576 # already built. Build them.
577
Doug Zongkerfc44a512014-08-26 13:10:25 -0700578 mappath = tempfile.mkstemp()[1]
579 OPTIONS.tempfiles.append(mappath)
580
Doug Zongker3c84f562014-07-31 11:06:30 -0700581 import add_img_to_target_files
582 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700583 path = add_img_to_target_files.BuildSystem(
584 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700585 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700586 path = add_img_to_target_files.BuildVendor(
587 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700588
Tao Baoff777812015-05-12 11:42:31 -0700589 # Bug: http://b/20939131
590 # In ext4 filesystems, block 0 might be changed even being mounted
591 # R/O. We add it to clobbered_blocks so that it will be written to the
592 # target unconditionally. Note that they are still part of care_map.
593 clobbered_blocks = "0"
594
595 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700596
597
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700598def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700599 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700600 # be installed on top of. For now, we expect the API just won't
601 # change very often. Similarly for fstab, it might have changed
602 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700603 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700604
Tao Bao838c68f2016-03-15 19:16:18 +0000605 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700606 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800607 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700608 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800609 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -0700610
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800611 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
612 OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700613 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800614 "pre-device": GetOemProperty("ro.product.device", oem_props,
615 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -0700616 OPTIONS.info_dict),
617 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
618 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700619
Doug Zongker05d3dea2009-06-22 11:32:31 -0700620 device_specific = common.DeviceSpecificParams(
621 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700622 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700623 output_zip=output_zip,
624 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700625 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700626 metadata=metadata,
627 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700628
Doug Zongkerc9253822014-02-04 12:17:58 -0800629 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800630 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800631
Tao Baod8d14be2016-02-04 14:26:02 -0800632 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
633
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700634 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
635 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
636 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700637
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800638 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700639 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800640
641 # Two-step package strategy (in chronological order, which is *not*
642 # the order in which the generated script has things):
643 #
644 # if stage is not "2/3" or "3/3":
645 # write recovery image to boot partition
646 # set stage to "2/3"
647 # reboot to boot partition and restart recovery
648 # else if stage is "2/3":
649 # write recovery image to recovery partition
650 # set stage to "3/3"
651 # reboot to recovery partition and restart recovery
652 # else:
653 # (stage must be "3/3")
654 # set stage to ""
655 # do normal full package installation:
656 # wipe and install system, boot image, etc.
657 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700658 # complete script normally
659 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800660
661 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
662 OPTIONS.input_tmp, "RECOVERY")
663 if OPTIONS.two_step:
664 if not OPTIONS.info_dict.get("multistage_support", None):
665 assert False, "two-step packages not supported by this build"
666 fs = OPTIONS.info_dict["fstab"]["/misc"]
667 assert fs.fs_type.upper() == "EMMC", \
668 "two-step packages only supported on devices with EMMC /misc partitions"
669 bcb_dev = {"bcb_dev": fs.device}
670 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
671 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700672if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800673""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800674
675 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
676 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800677 script.WriteRawImage("/recovery", "recovery.img")
678 script.AppendExtra("""
679set_stage("%(bcb_dev)s", "3/3");
680reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700681else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800682""" % bcb_dev)
683
Tao Baod42e97e2016-11-30 12:11:57 -0800684 # Stage 3/3: Make changes.
685 script.Comment("Stage 3/3")
686
Tao Bao6c55a8a2015-04-08 15:30:27 -0700687 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700688 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700689
Doug Zongkere5ff5902012-01-17 10:55:37 -0800690 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700691
Doug Zongker01ce19c2014-02-04 13:48:15 -0800692 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700693
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700694 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800695 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700696 if HasVendorPartition(input_zip):
697 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700698
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400699 # Place a copy of file_contexts.bin into the OTA package which will be used
700 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700701 if "selinux_fc" in OPTIONS.info_dict:
702 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500703
Michael Runge7cd99ba2014-10-22 17:21:48 -0700704 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
705
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700706 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700707 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800708
Doug Zongker26e66192014-02-20 13:22:07 -0800709 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700710 # Full OTA is done as an "incremental" against an empty source
711 # image. This has the effect of writing new data from the package
712 # to the entire partition, but lets us reuse the updater code that
713 # writes incrementals to do it.
714 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
715 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700716 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700717 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800718 else:
719 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700720 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800721 if not has_recovery_patch:
722 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800723 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700724
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700725 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800726 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700727
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700728 boot_img = common.GetBootableImage(
729 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800730
Doug Zongker91a99c22014-05-09 13:15:01 -0700731 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800732 def output_sink(fn, data):
733 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700734 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800735
736 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
737 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700738
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700739 system_items.GetMetadata(input_zip)
740 system_items.Get("system").SetPermissions(script)
741
742 if HasVendorPartition(input_zip):
743 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
744 script.ShowProgress(0.1, 0)
745
746 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700747 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
748 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700749 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700750 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700751 else:
752 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700753 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700754 script.UnpackPackageDir("vendor", "/vendor")
755
756 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
757 script.MakeSymlinks(symlinks)
758
759 vendor_items.GetMetadata(input_zip)
760 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700761
Doug Zongker37974732010-09-16 17:44:38 -0700762 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700763 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700764
Doug Zongker01ce19c2014-02-04 13:48:15 -0800765 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700766 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700767
Doug Zongker01ce19c2014-02-04 13:48:15 -0800768 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700769 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700770
Doug Zongker1c390a22009-05-14 19:06:36 -0700771 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700772 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700773
Doug Zongker14833602010-02-02 13:12:04 -0800774 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800775
Doug Zongker922206e2014-03-04 13:16:24 -0800776 if OPTIONS.wipe_user_data:
777 script.ShowProgress(0.1, 10)
778 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700779
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800780 if OPTIONS.two_step:
781 script.AppendExtra("""
782set_stage("%(bcb_dev)s", "");
783""" % bcb_dev)
784 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800785
786 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
787 script.Comment("Stage 1/3")
788 _WriteRecoveryImageToBoot(script, output_zip)
789
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800790 script.AppendExtra("""
791set_stage("%(bcb_dev)s", "2/3");
792reboot_now("%(bcb_dev)s", "");
793endif;
794endif;
795""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800796
Tao Bao5d182562016-02-23 11:38:39 -0800797 script.SetProgress(1)
798 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800799 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700800 WriteMetadata(metadata, output_zip)
801
Doug Zongkerfc44a512014-08-26 13:10:25 -0700802
Dan Albert8e0178d2015-01-27 15:53:15 -0800803def WritePolicyConfig(file_name, output_zip):
804 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500805
Doug Zongker2ea21062010-04-28 16:05:21 -0700806
807def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800808 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
809 common.ZipWriteStr(output_zip, METADATA_NAME, value,
810 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700811
Doug Zongkerfc44a512014-08-26 13:10:25 -0700812
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700813def LoadPartitionFiles(z, partition):
814 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700815 ZipFile, and return a dict of {filename: File object}."""
816 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700817 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700818 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700819 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700820 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700821 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700822 data = z.read(info.filename)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +0900823 out[fn] = common.File(fn, data, info.compress_size)
Doug Zongker1807e702012-02-28 12:21:08 -0800824 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700825
826
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700827def GetBuildProp(prop, info_dict):
828 """Return the fingerprint of the build of a given target-files info_dict."""
829 try:
830 return info_dict.get("build.prop", {})[prop]
831 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700832 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700833
Doug Zongkerfc44a512014-08-26 13:10:25 -0700834
Michael Runge4038aa82013-12-13 18:06:28 -0800835def AddToKnownPaths(filename, known_paths):
836 if filename[-1] == "/":
837 return
838 dirs = filename.split("/")[:-1]
839 while len(dirs) > 0:
840 path = "/".join(dirs)
841 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700842 break
Michael Runge4038aa82013-12-13 18:06:28 -0800843 known_paths.add(path)
844 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700845
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700846
Tao Baob31892e2017-02-07 11:21:17 -0800847def HandleDowngradeMetadata(metadata):
848 # Only incremental OTAs are allowed to reach here.
849 assert OPTIONS.incremental_source is not None
850
851 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
852 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
853 is_downgrade = long(post_timestamp) < long(pre_timestamp)
854
855 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800856 if not is_downgrade:
857 raise RuntimeError("--downgrade specified but no downgrade detected: "
858 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800859 metadata["ota-downgrade"] = "yes"
860 elif OPTIONS.timestamp:
861 if not is_downgrade:
862 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
863 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
864 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800865 else:
866 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800867 raise RuntimeError("Downgrade detected based on timestamp check: "
868 "pre: %s, post: %s. Need to specify --timestamp OR "
869 "--downgrade to allow building the incremental." % (
870 pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800871 metadata["post-timestamp"] = post_timestamp
872
873
Geremy Condra36bd3652014-02-06 19:45:10 -0800874def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700875 # TODO(tbao): We should factor out the common parts between
876 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800877 source_version = OPTIONS.source_info_dict["recovery_api_version"]
878 target_version = OPTIONS.target_info_dict["recovery_api_version"]
879
880 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700881 print("WARNING: generating edify script for a source that "
882 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700883 script = edify_generator.EdifyGenerator(
884 source_version, OPTIONS.target_info_dict,
885 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800886
Tao Bao3806c232015-07-05 21:08:33 -0700887 recovery_mount_options = OPTIONS.source_info_dict.get(
888 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700889 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
890 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800891 oem_dicts = None
892 if source_oem_props and target_oem_props:
893 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700894
Dan Albert8b72aef2015-03-23 19:13:21 -0700895 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700896 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800897 oem_dicts and oem_dicts[0],
898 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800899 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700900 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800901
Tao Baob31892e2017-02-07 11:21:17 -0800902 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800903
Geremy Condra36bd3652014-02-06 19:45:10 -0800904 device_specific = common.DeviceSpecificParams(
905 source_zip=source_zip,
906 source_version=source_version,
907 target_zip=target_zip,
908 target_version=target_version,
909 output_zip=output_zip,
910 script=script,
911 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700912 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800913
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800914 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700915 OPTIONS.source_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800916 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700917 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800918 metadata["pre-build"] = source_fp
919 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700920 metadata["pre-build-incremental"] = GetBuildProp(
921 "ro.build.version.incremental", OPTIONS.source_info_dict)
922 metadata["post-build-incremental"] = GetBuildProp(
923 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800924
925 source_boot = common.GetBootableImage(
926 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
927 OPTIONS.source_info_dict)
928 target_boot = common.GetBootableImage(
929 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
930 updating_boot = (not OPTIONS.two_step and
931 (source_boot.data != target_boot.data))
932
Geremy Condra36bd3652014-02-06 19:45:10 -0800933 target_recovery = common.GetBootableImage(
934 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800935
Doug Zongkerfc44a512014-08-26 13:10:25 -0700936 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
937 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700938
939 blockimgdiff_version = 1
940 if OPTIONS.info_dict:
941 blockimgdiff_version = max(
942 int(i) for i in
943 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
944
Tao Baof8acad12016-07-07 09:09:58 -0700945 # Check the first block of the source system partition for remount R/W only
946 # if the filesystem is ext4.
947 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
948 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700949 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
950 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
951 # b) the blocks listed in block map may not contain all the bytes for a given
952 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700953 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
954 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
955 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700956 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800957 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700958 version=blockimgdiff_version,
959 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700960
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700961 if HasVendorPartition(target_zip):
962 if not HasVendorPartition(source_zip):
963 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700964 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
965 OPTIONS.source_info_dict)
966 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
967 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800968
969 # Check first block of vendor partition for remount R/W only if
970 # disk type is ext4
971 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800972 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700973 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700974 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800975 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700976 version=blockimgdiff_version,
977 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700978 else:
979 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800980
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800981 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800982 device_specific.IncrementalOTA_Assertions()
983
984 # Two-step incremental package strategy (in chronological order,
985 # which is *not* the order in which the generated script has
986 # things):
987 #
988 # if stage is not "2/3" or "3/3":
989 # do verification on current system
990 # write recovery image to boot partition
991 # set stage to "2/3"
992 # reboot to boot partition and restart recovery
993 # else if stage is "2/3":
994 # write recovery image to recovery partition
995 # set stage to "3/3"
996 # reboot to recovery partition and restart recovery
997 # else:
998 # (stage must be "3/3")
999 # perform update:
1000 # patch system files, etc.
1001 # force full install of new boot image
1002 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001003 # complete script normally
1004 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001005
1006 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001007 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -08001008 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001009 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001010 assert fs.fs_type.upper() == "EMMC", \
1011 "two-step packages only supported on devices with EMMC /misc partitions"
1012 bcb_dev = {"bcb_dev": fs.device}
1013 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1014 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001015if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001016""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001017
1018 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1019 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001020 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001021 script.WriteRawImage("/recovery", "recovery.img")
1022 script.AppendExtra("""
1023set_stage("%(bcb_dev)s", "3/3");
1024reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001025else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001026""" % bcb_dev)
1027
Tao Baod42e97e2016-11-30 12:11:57 -08001028 # Stage 1/3: (a) Verify the current system.
1029 script.Comment("Stage 1/3")
1030
Tao Bao6c55a8a2015-04-08 15:30:27 -07001031 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -08001032 script.Print("Source: %s" % (source_fp,))
1033 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001034
Geremy Condra36bd3652014-02-06 19:45:10 -08001035 script.Print("Verifying current system...")
1036
1037 device_specific.IncrementalOTA_VerifyBegin()
1038
Tao Bao3e30d972016-03-15 13:20:19 -07001039 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1040 # patching on a device that's already on the target build will damage the
1041 # system. Because operations like move don't check the block state, they
1042 # always apply the changes unconditionally.
1043 if blockimgdiff_version <= 2:
1044 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001045 script.AssertSomeFingerprint(source_fp)
1046 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001047 script.AssertSomeThumbprint(
1048 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001049
1050 else: # blockimgdiff_version > 2
1051 if source_oem_props is None and target_oem_props is None:
1052 script.AssertSomeFingerprint(source_fp, target_fp)
1053 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001054 script.AssertSomeThumbprint(
1055 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1056 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001057 elif source_oem_props is None and target_oem_props is not None:
1058 script.AssertFingerprintOrThumbprint(
1059 source_fp,
1060 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1061 else:
1062 script.AssertFingerprintOrThumbprint(
1063 target_fp,
1064 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001065
Tao Baod8d14be2016-02-04 14:26:02 -08001066 # Check the required cache size (i.e. stashed blocks).
1067 size = []
1068 if system_diff:
1069 size.append(system_diff.required_cache)
1070 if vendor_diff:
1071 size.append(vendor_diff.required_cache)
1072
Geremy Condra36bd3652014-02-06 19:45:10 -08001073 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001074 boot_type, boot_device = common.GetTypeAndDevice(
1075 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001076 d = common.Difference(target_boot, source_boot)
1077 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001078 if d is None:
1079 include_full_boot = True
1080 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1081 else:
1082 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001083
Tao Bao89fbb0f2017-01-10 10:47:58 -08001084 print("boot target: %d source: %d diff: %d" % (
1085 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001086
Doug Zongkerf8340082014-08-05 10:39:37 -07001087 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001088
Doug Zongkerf8340082014-08-05 10:39:37 -07001089 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1090 (boot_type, boot_device,
1091 source_boot.size, source_boot.sha1,
1092 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001093 size.append(target_boot.size)
1094
1095 if size:
1096 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001097
1098 device_specific.IncrementalOTA_VerifyEnd()
1099
1100 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001101 # Stage 1/3: (b) Write recovery image to /boot.
1102 _WriteRecoveryImageToBoot(script, output_zip)
1103
Geremy Condra36bd3652014-02-06 19:45:10 -08001104 script.AppendExtra("""
1105set_stage("%(bcb_dev)s", "2/3");
1106reboot_now("%(bcb_dev)s", "");
1107else
1108""" % bcb_dev)
1109
Tao Baod42e97e2016-11-30 12:11:57 -08001110 # Stage 3/3: Make changes.
1111 script.Comment("Stage 3/3")
1112
Jesse Zhao75bcea02015-01-06 10:59:53 -08001113 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001114 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001115 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001116 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001117
Geremy Condra36bd3652014-02-06 19:45:10 -08001118 script.Comment("---- start making changes here ----")
1119
1120 device_specific.IncrementalOTA_InstallBegin()
1121
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001122 system_diff.WriteScript(script, output_zip,
1123 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001124
Doug Zongkerfc44a512014-08-26 13:10:25 -07001125 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001126 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001127
1128 if OPTIONS.two_step:
1129 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1130 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001131 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001132
1133 if not OPTIONS.two_step:
1134 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001135 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001136 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001137 script.Print("Installing boot image...")
1138 script.WriteRawImage("/boot", "boot.img")
1139 else:
1140 # Produce the boot image by applying a patch to the current
1141 # contents of the boot partition, and write it back to the
1142 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001143 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001144 script.Print("Patching boot image...")
1145 script.ShowProgress(0.1, 10)
1146 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1147 % (boot_type, boot_device,
1148 source_boot.size, source_boot.sha1,
1149 target_boot.size, target_boot.sha1),
1150 "-",
1151 target_boot.size, target_boot.sha1,
1152 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001153 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001154 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001155
1156 # Do device-specific installation (eg, write radio image).
1157 device_specific.IncrementalOTA_InstallEnd()
1158
1159 if OPTIONS.extra_script is not None:
1160 script.AppendExtra(OPTIONS.extra_script)
1161
Doug Zongker922206e2014-03-04 13:16:24 -08001162 if OPTIONS.wipe_user_data:
1163 script.Print("Erasing user data...")
1164 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001165 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001166
Geremy Condra36bd3652014-02-06 19:45:10 -08001167 if OPTIONS.two_step:
1168 script.AppendExtra("""
1169set_stage("%(bcb_dev)s", "");
1170endif;
1171endif;
1172""" % bcb_dev)
1173
1174 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001175 # For downgrade OTAs, we prefer to use the update-binary in the source
1176 # build that is actually newer than the one in the target build.
1177 if OPTIONS.downgrade:
1178 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1179 else:
1180 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001181 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001182 WriteMetadata(metadata, output_zip)
1183
Doug Zongker32b527d2014-03-04 10:03:02 -08001184
Tao Bao9bc6bb22015-11-09 16:58:28 -08001185def WriteVerifyPackage(input_zip, output_zip):
1186 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1187
1188 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1189 recovery_mount_options = OPTIONS.info_dict.get(
1190 "recovery_mount_options")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001191 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -07001192 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001193 oem_dicts = _LoadOemDicts(script, oem_props, recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001194
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001195 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
1196 OPTIONS.info_dict)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001197 metadata = {
1198 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001199 "pre-device": GetOemProperty("ro.product.device", oem_props,
1200 oem_dicts and oem_dicts[0],
Tao Bao9bc6bb22015-11-09 16:58:28 -08001201 OPTIONS.info_dict),
1202 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1203 }
1204
1205 device_specific = common.DeviceSpecificParams(
1206 input_zip=input_zip,
1207 input_version=OPTIONS.info_dict["recovery_api_version"],
1208 output_zip=output_zip,
1209 script=script,
1210 input_tmp=OPTIONS.input_tmp,
1211 metadata=metadata,
1212 info_dict=OPTIONS.info_dict)
1213
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001214 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001215
1216 script.Print("Verifying device images against %s..." % target_fp)
1217 script.AppendExtra("")
1218
1219 script.Print("Verifying boot...")
1220 boot_img = common.GetBootableImage(
1221 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1222 boot_type, boot_device = common.GetTypeAndDevice(
1223 "/boot", OPTIONS.info_dict)
1224 script.Verify("%s:%s:%d:%s" % (
1225 boot_type, boot_device, boot_img.size, boot_img.sha1))
1226 script.AppendExtra("")
1227
1228 script.Print("Verifying recovery...")
1229 recovery_img = common.GetBootableImage(
1230 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1231 recovery_type, recovery_device = common.GetTypeAndDevice(
1232 "/recovery", OPTIONS.info_dict)
1233 script.Verify("%s:%s:%d:%s" % (
1234 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1235 script.AppendExtra("")
1236
1237 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1238 system_tgt.ResetFileMap()
1239 system_diff = common.BlockDifference("system", system_tgt, src=None)
1240 system_diff.WriteStrictVerifyScript(script)
1241
1242 if HasVendorPartition(input_zip):
1243 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1244 vendor_tgt.ResetFileMap()
1245 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1246 vendor_diff.WriteStrictVerifyScript(script)
1247
1248 # Device specific partitions, such as radio, bootloader and etc.
1249 device_specific.VerifyOTA_Assertions()
1250
1251 script.SetProgress(1.0)
1252 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001253 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001254 WriteMetadata(metadata, output_zip)
1255
1256
Tao Baoc098e9e2016-01-07 13:03:56 -08001257def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1258 source_file=None):
1259 """Generate an Android OTA package that has A/B update payload."""
1260
Tao Bao2dd1c482017-02-03 16:49:39 -08001261 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1262 expected_length=None):
1263 """Compute the streaming metadata for a given zip.
1264
1265 When 'reserve_space' is True, we reserve extra space for the offset and
1266 length of the metadata entry itself, although we don't know the final
1267 values until the package gets signed. This function will be called again
1268 after signing. We then write the actual values and pad the string to the
1269 length we set earlier. Note that we can't use the actual length of the
1270 metadata entry in the second run. Otherwise the offsets for other entries
1271 will be changing again.
1272 """
Tao Baoc96316c2017-01-24 22:10:49 -08001273
1274 def ComputeEntryOffsetSize(name):
1275 """Compute the zip entry offset and size."""
1276 info = zip_file.getinfo(name)
1277 offset = info.header_offset + len(info.FileHeader())
1278 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001279 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001280
1281 # payload.bin and payload_properties.txt must exist.
1282 offsets = [ComputeEntryOffsetSize('payload.bin'),
1283 ComputeEntryOffsetSize('payload_properties.txt')]
1284
1285 # care_map.txt is available only if dm-verity is enabled.
1286 if 'care_map.txt' in zip_file.namelist():
1287 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001288
1289 # 'META-INF/com/android/metadata' is required. We don't know its actual
1290 # offset and length (as well as the values for other entries). So we
1291 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1292 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1293 # beginning of the zip), as well as the possible value changes in other
1294 # entries.
1295 if reserve_space:
1296 offsets.append('metadata:' + ' ' * 10)
1297 else:
1298 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1299
1300 value = ','.join(offsets)
1301 if expected_length is not None:
1302 assert len(value) <= expected_length, \
1303 'Insufficient reserved space: reserved=%d, actual=%d' % (
1304 expected_length, len(value))
1305 value += ' ' * (expected_length - len(value))
1306 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001307
Alex Deymod8d96ec2016-06-10 16:38:31 -07001308 # The place where the output from the subprocess should go.
1309 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1310
Tao Baoc098e9e2016-01-07 13:03:56 -08001311 # Setup signing keys.
1312 if OPTIONS.package_key is None:
1313 OPTIONS.package_key = OPTIONS.info_dict.get(
1314 "default_system_dev_certificate",
1315 "build/target/product/security/testkey")
1316
Tao Baodea0f8b2016-06-20 17:55:06 -07001317 # A/B updater expects a signing key in RSA format. Gets the key ready for
1318 # later use in step 3, unless a payload_signer has been specified.
1319 if OPTIONS.payload_signer is None:
1320 cmd = ["openssl", "pkcs8",
1321 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1322 "-inform", "DER", "-nocrypt"]
1323 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1324 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001325 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1326 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001327 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001328
Tao Baodea0f8b2016-06-20 17:55:06 -07001329 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001330 temp_zip_file = tempfile.NamedTemporaryFile()
1331 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1332 compression=zipfile.ZIP_DEFLATED)
1333
1334 # Metadata to comply with Android OTA package format.
1335 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001336 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001337 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001338 oem_dicts = _LoadOemDicts(script, None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001339
1340 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001341 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001342 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001343 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1344 OPTIONS.info_dict),
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001345 "pre-device": GetOemProperty("ro.product.device", oem_props,
1346 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001347 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001348 "ota-required-cache": "0",
1349 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001350 }
1351
1352 if source_file is not None:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001353 metadata["pre-build"] = CalculateFingerprint(oem_props,
1354 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001355 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001356 metadata["pre-build-incremental"] = GetBuildProp(
1357 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001358
Tao Baob31892e2017-02-07 11:21:17 -08001359 HandleDowngradeMetadata(metadata)
1360 else:
1361 metadata["post-timestamp"] = GetBuildProp(
1362 "ro.build.date.utc", OPTIONS.info_dict)
1363
Tao Baoc098e9e2016-01-07 13:03:56 -08001364 # 1. Generate payload.
1365 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1366 cmd = ["brillo_update_payload", "generate",
1367 "--payload", payload_file,
1368 "--target_image", target_file]
1369 if source_file is not None:
1370 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001371 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1372 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001373 assert p1.returncode == 0, "brillo_update_payload generate failed"
1374
1375 # 2. Generate hashes of the payload and metadata files.
1376 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1377 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1378 cmd = ["brillo_update_payload", "hash",
1379 "--unsigned_payload", payload_file,
1380 "--signature_size", "256",
1381 "--metadata_hash_file", metadata_sig_file,
1382 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001383 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1384 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001385 assert p1.returncode == 0, "brillo_update_payload hash failed"
1386
1387 # 3. Sign the hashes and insert them back into the payload file.
1388 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1389 suffix=".bin")
1390 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1391 suffix=".bin")
1392 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001393 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001394 cmd = [OPTIONS.payload_signer]
1395 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001396 else:
1397 cmd = ["openssl", "pkeyutl", "-sign",
1398 "-inkey", rsa_key,
1399 "-pkeyopt", "digest:sha256"]
1400 cmd.extend(["-in", payload_sig_file,
1401 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001402 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1403 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001404 assert p1.returncode == 0, "openssl sign payload failed"
1405
1406 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001407 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001408 cmd = [OPTIONS.payload_signer]
1409 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001410 else:
1411 cmd = ["openssl", "pkeyutl", "-sign",
1412 "-inkey", rsa_key,
1413 "-pkeyopt", "digest:sha256"]
1414 cmd.extend(["-in", metadata_sig_file,
1415 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001416 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1417 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001418 assert p1.returncode == 0, "openssl sign metadata failed"
1419
1420 # 3c. Insert the signatures back into the payload file.
1421 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1422 suffix=".bin")
1423 cmd = ["brillo_update_payload", "sign",
1424 "--unsigned_payload", payload_file,
1425 "--payload", signed_payload_file,
1426 "--signature_size", "256",
1427 "--metadata_signature_file", signed_metadata_sig_file,
1428 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001429 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1430 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001431 assert p1.returncode == 0, "brillo_update_payload sign failed"
1432
Alex Deymo19241c12016-02-04 22:29:29 -08001433 # 4. Dump the signed payload properties.
1434 properties_file = common.MakeTempFile(prefix="payload-properties-",
1435 suffix=".txt")
1436 cmd = ["brillo_update_payload", "properties",
1437 "--payload", signed_payload_file,
1438 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001439 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1440 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001441 assert p1.returncode == 0, "brillo_update_payload properties failed"
1442
Tao Bao7c5dc572016-06-14 17:48:11 -07001443 if OPTIONS.wipe_user_data:
1444 with open(properties_file, "a") as f:
1445 f.write("POWERWASH=1\n")
1446 metadata["ota-wipe"] = "yes"
1447
Tao Baoc96316c2017-01-24 22:10:49 -08001448 # Add the signed payload file and properties into the zip. In order to
1449 # support streaming, we pack payload.bin, payload_properties.txt and
1450 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1451 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001452 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1453 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001454 common.ZipWrite(output_zip, properties_file,
1455 arcname="payload_properties.txt",
1456 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001457
Tianjie Xucfa86222016-03-07 16:31:19 -08001458 # If dm-verity is supported for the device, copy contents of care_map
1459 # into A/B OTA package.
1460 if OPTIONS.info_dict.get("verity") == "true":
1461 target_zip = zipfile.ZipFile(target_file, "r")
1462 care_map_path = "META/care_map.txt"
1463 namelist = target_zip.namelist()
1464 if care_map_path in namelist:
1465 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001466 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1467 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001468 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001469 print("Warning: cannot find care map file in target_file package")
Tianjie Xucfa86222016-03-07 16:31:19 -08001470 common.ZipClose(target_zip)
1471
Tao Bao2dd1c482017-02-03 16:49:39 -08001472 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001473 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001474 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001475 WriteMetadata(metadata, output_zip)
1476 common.ZipClose(output_zip)
1477
Tao Bao2dd1c482017-02-03 16:49:39 -08001478 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1479 # zip entries, as well as padding the entry headers. We do a preliminary
1480 # signing (with an incomplete metadata entry) to allow that to happen. Then
1481 # compute the zip entry offsets, write back the final metadata and do the
1482 # final signing.
1483 prelim_signing = tempfile.NamedTemporaryFile()
1484 SignOutput(temp_zip_file.name, prelim_signing.name)
1485 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001486
Tao Bao2dd1c482017-02-03 16:49:39 -08001487 # Open the signed zip. Compute the final metadata that's needed for streaming.
1488 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1489 compression=zipfile.ZIP_DEFLATED)
1490 expected_length = len(metadata['ota-streaming-property-files'])
1491 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1492 prelim_zip, reserve_space=False, expected_length=expected_length)
1493
1494 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1495 final_signing = tempfile.NamedTemporaryFile()
1496 output_zip = zipfile.ZipFile(final_signing, "w",
1497 compression=zipfile.ZIP_DEFLATED)
1498 for item in prelim_zip.infolist():
1499 if item.filename == METADATA_NAME:
1500 continue
1501
1502 data = prelim_zip.read(item.filename)
1503 out_info = copy.copy(item)
1504 common.ZipWriteStr(output_zip, out_info, data)
1505
1506 # Now write the final metadata entry.
1507 WriteMetadata(metadata, output_zip)
1508 common.ZipClose(prelim_zip)
1509 common.ZipClose(output_zip)
1510
1511 # Re-sign the package after updating the metadata entry.
1512 SignOutput(final_signing.name, output_file)
1513 final_signing.close()
1514
1515 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001516 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001517 actual = metadata['ota-streaming-property-files'].strip()
1518 expected = ComputeStreamingMetadata(output_zip)
1519 assert actual == expected, \
1520 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001521 common.ZipClose(output_zip)
1522
Tao Baoc098e9e2016-01-07 13:03:56 -08001523
Dan Albert8b72aef2015-03-23 19:13:21 -07001524class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001525 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001526 self.deferred_patch_list = None
Tao Bao89fbb0f2017-01-10 10:47:58 -08001527 print("Loading target...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001528 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001529 print("Loading source...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001530 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1531
1532 self.verbatim_targets = verbatim_targets = []
1533 self.patch_list = patch_list = []
1534 diffs = []
1535 self.renames = renames = {}
1536 known_paths = set()
1537 largest_source_size = 0
1538
1539 matching_file_cache = {}
1540 for fn, sf in source_data.items():
1541 assert fn == sf.name
1542 matching_file_cache["path:" + fn] = sf
1543 if fn in target_data.keys():
1544 AddToKnownPaths(fn, known_paths)
1545 # Only allow eligibility for filename/sha matching
1546 # if there isn't a perfect path match.
1547 if target_data.get(sf.name) is None:
1548 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1549 matching_file_cache["sha:" + sf.sha1] = sf
1550
1551 for fn in sorted(target_data.keys()):
1552 tf = target_data[fn]
1553 assert fn == tf.name
1554 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1555 if sf is not None and sf.name != tf.name:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001556 print("File has moved from " + sf.name + " to " + tf.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001557 renames[sf.name] = tf
1558
1559 if sf is None or fn in OPTIONS.require_verbatim:
1560 # This file should be included verbatim
1561 if fn in OPTIONS.prohibit_verbatim:
1562 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Tao Bao89fbb0f2017-01-10 10:47:58 -08001563 print("send", fn, "verbatim")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001564 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001565 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001566 if fn in target_data.keys():
1567 AddToKnownPaths(fn, known_paths)
1568 elif tf.sha1 != sf.sha1:
1569 # File is different; consider sending as a patch
1570 diffs.append(common.Difference(tf, sf))
1571 else:
1572 # Target file data identical to source (may still be renamed)
1573 pass
1574
1575 common.ComputeDifferences(diffs)
1576
1577 for diff in diffs:
1578 tf, sf, d = diff.GetPatch()
1579 path = "/".join(tf.name.split("/")[:-1])
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001580 if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001581 path not in known_paths:
1582 # patch is almost as big as the file; don't bother patching
1583 # or a patch + rename cannot take place due to the target
1584 # directory not existing
1585 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001586 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001587 if sf.name in renames:
1588 del renames[sf.name]
1589 AddToKnownPaths(tf.name, known_paths)
1590 else:
1591 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1592 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1593 largest_source_size = max(largest_source_size, sf.size)
1594
1595 self.largest_source_size = largest_source_size
1596
1597 def EmitVerification(self, script):
1598 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001599 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001600 if tf.name != sf.name:
1601 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1602 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1603 so_far += sf.size
1604 return so_far
1605
Michael Runge63f01de2014-10-28 19:24:19 -07001606 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001607 for fn, _, sha1 in self.verbatim_targets:
1608 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001609 script.FileCheck("/"+fn, sha1)
1610 for tf, _, _, _ in self.patch_list:
1611 script.FileCheck(tf.name, tf.sha1)
1612
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001613 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001614 file_list = ["/" + i[0] for i in self.verbatim_targets]
1615 file_list += ["/" + i for i in self.source_data
1616 if i not in self.target_data and i not in self.renames]
1617 file_list += list(extras)
1618 # Sort the list in descending order, which removes all the files first
1619 # before attempting to remove the folder. (Bug: 22960996)
1620 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001621
1622 def TotalPatchSize(self):
1623 return sum(i[1].size for i in self.patch_list)
1624
1625 def EmitPatches(self, script, total_patch_size, so_far):
1626 self.deferred_patch_list = deferred_patch_list = []
1627 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001628 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001629 if tf.name == "system/build.prop":
1630 deferred_patch_list.append(item)
1631 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001632 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001633 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001634 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1635 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001636 so_far += tf.size
1637 script.SetProgress(so_far / total_patch_size)
1638 return so_far
1639
1640 def EmitDeferredPatches(self, script):
1641 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001642 tf, sf, _, _ = item
1643 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1644 "patch/" + sf.name + ".p")
1645 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001646
1647 def EmitRenames(self, script):
1648 if len(self.renames) > 0:
1649 script.Print("Renaming files...")
1650 for src, tgt in self.renames.iteritems():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001651 print("Renaming " + src + " to " + tgt.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001652 script.RenameFile(src, tgt.name)
1653
1654
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001655def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001656 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1657 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1658
Doug Zongker26e66192014-02-20 13:22:07 -08001659 if (OPTIONS.block_based and
1660 target_has_recovery_patch and
1661 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001662 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1663
Doug Zongker37974732010-09-16 17:44:38 -07001664 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1665 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001666
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001667 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001668 print("WARNING: generating edify script for a source that "
1669 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001670 script = edify_generator.EdifyGenerator(
1671 source_version, OPTIONS.target_info_dict,
1672 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001673
Tao Bao34b47bf2015-06-22 19:17:41 -07001674 recovery_mount_options = OPTIONS.source_info_dict.get(
1675 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001676 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1677 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001678 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -07001679 if source_oem_props or target_oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001680 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -07001681
Dan Albert8b72aef2015-03-23 19:13:21 -07001682 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001683 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001684 oem_dicts and oem_dicts[0],
1685 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001686 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001687 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001688
Tao Baob31892e2017-02-07 11:21:17 -08001689 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -08001690
Doug Zongker05d3dea2009-06-22 11:32:31 -07001691 device_specific = common.DeviceSpecificParams(
1692 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001693 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001694 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001695 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001696 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001697 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001698 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001699 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001700
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001701 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001702 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001703 if HasVendorPartition(target_zip):
1704 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001705 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001706 else:
1707 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001708
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001709 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001710 OPTIONS.target_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001711 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001712 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001713
Tao Bao3e30d972016-03-15 13:20:19 -07001714 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001715 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001716 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001717 script.AssertSomeThumbprint(
1718 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1719 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001720 elif source_oem_props is None and target_oem_props is not None:
1721 script.AssertFingerprintOrThumbprint(
1722 source_fp,
1723 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1724 else:
1725 script.AssertFingerprintOrThumbprint(
1726 target_fp,
1727 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001728
Doug Zongker2ea21062010-04-28 16:05:21 -07001729 metadata["pre-build"] = source_fp
1730 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001731 metadata["pre-build-incremental"] = GetBuildProp(
1732 "ro.build.version.incremental", OPTIONS.source_info_dict)
1733 metadata["post-build-incremental"] = GetBuildProp(
1734 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001735
Doug Zongker55d93282011-01-25 17:03:34 -08001736 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001737 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1738 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001739 target_boot = common.GetBootableImage(
1740 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001741 updating_boot = (not OPTIONS.two_step and
1742 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001743
Doug Zongker55d93282011-01-25 17:03:34 -08001744 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001745 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1746 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001747 target_recovery = common.GetBootableImage(
1748 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001749 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001750
Doug Zongker881dd402009-09-20 14:03:55 -07001751 # Here's how we divide up the progress bar:
1752 # 0.1 for verifying the start state (PatchCheck calls)
1753 # 0.8 for applying patches (ApplyPatch calls)
1754 # 0.1 for unpacking verbatim files, symlinking, and doing the
1755 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001756
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001757 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001758 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001759
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001760 # Two-step incremental package strategy (in chronological order,
1761 # which is *not* the order in which the generated script has
1762 # things):
1763 #
1764 # if stage is not "2/3" or "3/3":
1765 # do verification on current system
1766 # write recovery image to boot partition
1767 # set stage to "2/3"
1768 # reboot to boot partition and restart recovery
1769 # else if stage is "2/3":
1770 # write recovery image to recovery partition
1771 # set stage to "3/3"
1772 # reboot to recovery partition and restart recovery
1773 # else:
1774 # (stage must be "3/3")
1775 # perform update:
1776 # patch system files, etc.
1777 # force full install of new boot image
1778 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001779 # complete script normally
1780 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001781
1782 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001783 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001784 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001785 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001786 assert fs.fs_type.upper() == "EMMC", \
1787 "two-step packages only supported on devices with EMMC /misc partitions"
1788 bcb_dev = {"bcb_dev": fs.device}
1789 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1790 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001791if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001792""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001793
1794 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1795 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001796 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001797 script.WriteRawImage("/recovery", "recovery.img")
1798 script.AppendExtra("""
1799set_stage("%(bcb_dev)s", "3/3");
1800reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001801else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001802""" % bcb_dev)
1803
Tao Baod42e97e2016-11-30 12:11:57 -08001804 # Stage 1/3: (a) Verify the current system.
1805 script.Comment("Stage 1/3")
1806
Tao Bao6c55a8a2015-04-08 15:30:27 -07001807 # Dump fingerprints
1808 script.Print("Source: %s" % (source_fp,))
1809 script.Print("Target: %s" % (target_fp,))
1810
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001811 script.Print("Verifying current system...")
1812
Doug Zongkere5ff5902012-01-17 10:55:37 -08001813 device_specific.IncrementalOTA_VerifyBegin()
1814
Doug Zongker881dd402009-09-20 14:03:55 -07001815 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001816 so_far = system_diff.EmitVerification(script)
1817 if vendor_diff:
1818 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001819
Tao Baod8d14be2016-02-04 14:26:02 -08001820 size = []
1821 if system_diff.patch_list:
1822 size.append(system_diff.largest_source_size)
1823 if vendor_diff:
1824 if vendor_diff.patch_list:
1825 size.append(vendor_diff.largest_source_size)
1826
Doug Zongker5da317e2009-06-02 13:38:17 -07001827 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001828 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001829 _, _, d = d.ComputePatch()
Tao Bao89fbb0f2017-01-10 10:47:58 -08001830 print("boot target: %d source: %d diff: %d" % (
1831 target_boot.size, source_boot.size, len(d)))
Doug Zongker5da317e2009-06-02 13:38:17 -07001832
Doug Zongker048e7ca2009-06-15 14:31:53 -07001833 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001834
Tao Baodd24da92015-07-29 14:09:23 -07001835 boot_type, boot_device = common.GetTypeAndDevice(
1836 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001837
1838 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1839 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001840 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001841 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001842 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001843 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001844
Tao Baod8d14be2016-02-04 14:26:02 -08001845 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001846 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001847
Doug Zongker05d3dea2009-06-22 11:32:31 -07001848 device_specific.IncrementalOTA_VerifyEnd()
1849
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001850 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001851 # Stage 1/3: (b) Write recovery image to /boot.
1852 _WriteRecoveryImageToBoot(script, output_zip)
1853
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001854 script.AppendExtra("""
1855set_stage("%(bcb_dev)s", "2/3");
1856reboot_now("%(bcb_dev)s", "");
1857else
1858""" % bcb_dev)
1859
Tao Baod42e97e2016-11-30 12:11:57 -08001860 # Stage 3/3: Make changes.
1861 script.Comment("Stage 3/3")
1862
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001863 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001864
Doug Zongkere5ff5902012-01-17 10:55:37 -08001865 device_specific.IncrementalOTA_InstallBegin()
1866
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001867 if OPTIONS.two_step:
1868 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1869 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001870 print("writing full boot image (forced by two-step mode)")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001871
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001872 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001873 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1874 if vendor_diff:
1875 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001876
Doug Zongker881dd402009-09-20 14:03:55 -07001877 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001878 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1879 if vendor_diff:
1880 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001881 if updating_boot:
1882 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001883
1884 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001885 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1886 if vendor_diff:
1887 script.Print("Patching vendor files...")
1888 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001889
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001890 if not OPTIONS.two_step:
1891 if updating_boot:
1892 # Produce the boot image by applying a patch to the current
1893 # contents of the boot partition, and write it back to the
1894 # partition.
1895 script.Print("Patching boot image...")
1896 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1897 % (boot_type, boot_device,
1898 source_boot.size, source_boot.sha1,
1899 target_boot.size, target_boot.sha1),
1900 "-",
1901 target_boot.size, target_boot.sha1,
1902 source_boot.sha1, "patch/boot.img.p")
1903 so_far += target_boot.size
1904 script.SetProgress(so_far / total_patch_size)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001905 print("boot image changed; including.")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001906 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001907 print("boot image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001908
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001909 system_items = ItemSet("system", "META/filesystem_config.txt")
1910 if vendor_diff:
1911 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1912
Doug Zongkereef39442009-04-02 12:14:19 -07001913 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001914 # Recovery is generated as a patch using both the boot image
1915 # (which contains the same linux kernel as recovery) and the file
1916 # /system/etc/recovery-resource.dat (which contains all the images
1917 # used in the recovery UI) as sources. This lets us minimize the
1918 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001919 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001920 # For older builds where recovery-resource.dat is not present, we
1921 # use only the boot image as the source.
1922
Doug Zongkerc9253822014-02-04 12:17:58 -08001923 if not target_has_recovery_patch:
1924 def output_sink(fn, data):
1925 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001926 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001927
1928 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1929 target_recovery, target_boot)
1930 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001931 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001932 "/system/etc/install-recovery.sh"])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001933 print("recovery image changed; including as patch from boot.")
Doug Zongkereef39442009-04-02 12:14:19 -07001934 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001935 print("recovery image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001936
Doug Zongker881dd402009-09-20 14:03:55 -07001937 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001938
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001939 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1940 if vendor_diff:
1941 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1942
1943 temp_script = script.MakeTemporary()
1944 system_items.GetMetadata(target_zip)
1945 system_items.Get("system").SetPermissions(temp_script)
1946 if vendor_diff:
1947 vendor_items.GetMetadata(target_zip)
1948 vendor_items.Get("vendor").SetPermissions(temp_script)
1949
1950 # Note that this call will mess up the trees of Items, so make sure
1951 # we're done with them.
1952 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1953 if vendor_diff:
1954 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001955
1956 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001957 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1958
1959 # Delete all the symlinks in source that aren't in target. This
1960 # needs to happen before verbatim files are unpacked, in case a
1961 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001962
1963 # If a symlink in the source will be replaced by a regular file, we cannot
1964 # delete the symlink/file in case the package gets applied again. For such
1965 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1966 # (Bug: 23646151)
1967 replaced_symlinks = dict()
1968 if system_diff:
1969 for i in system_diff.verbatim_targets:
1970 replaced_symlinks["/%s" % (i[0],)] = i[2]
1971 if vendor_diff:
1972 for i in vendor_diff.verbatim_targets:
1973 replaced_symlinks["/%s" % (i[0],)] = i[2]
1974
1975 if system_diff:
1976 for tf in system_diff.renames.values():
1977 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1978 if vendor_diff:
1979 for tf in vendor_diff.renames.values():
1980 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1981
1982 always_delete = []
1983 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001984 for dest, link in source_symlinks:
1985 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001986 if link in replaced_symlinks:
1987 may_delete.append((link, replaced_symlinks[link]))
1988 else:
1989 always_delete.append(link)
1990 script.DeleteFiles(always_delete)
1991 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001992
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001993 if system_diff.verbatim_targets:
1994 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001995 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001996 if vendor_diff and vendor_diff.verbatim_targets:
1997 script.Print("Unpacking new vendor files...")
1998 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001999
Doug Zongkerc9253822014-02-04 12:17:58 -08002000 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08002001 script.Print("Unpacking new recovery...")
2002 script.UnpackPackageDir("recovery", "/system")
2003
Doug Zongkerc8b4e842014-06-16 15:16:31 -07002004 system_diff.EmitRenames(script)
2005 if vendor_diff:
2006 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08002007
Doug Zongker05d3dea2009-06-22 11:32:31 -07002008 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07002009
2010 # Create all the symlinks that don't already exist, or point to
2011 # somewhere different than what we want. Delete each symlink before
2012 # creating it, since the 'symlink' command won't overwrite.
2013 to_create = []
2014 for dest, link in target_symlinks:
2015 if link in source_symlinks_d:
2016 if dest != source_symlinks_d[link]:
2017 to_create.append((dest, link))
2018 else:
2019 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002020 script.DeleteFiles([i[1] for i in to_create])
2021 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07002022
2023 # Now that the symlinks are created, we can set all the
2024 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002025 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07002026
Doug Zongker881dd402009-09-20 14:03:55 -07002027 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07002028 device_specific.IncrementalOTA_InstallEnd()
2029
Doug Zongker1c390a22009-05-14 19:06:36 -07002030 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07002031 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07002032
Doug Zongkere92f15a2011-08-26 13:46:40 -07002033 # Patch the build.prop file last, so if something fails but the
2034 # device can still come up, it appears to be the old build and will
2035 # get set the OTA package again to retry.
2036 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07002037 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07002038
Doug Zongker922206e2014-03-04 13:16:24 -08002039 if OPTIONS.wipe_user_data:
2040 script.Print("Erasing user data...")
2041 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08002042 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08002043
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002044 if OPTIONS.two_step:
2045 script.AppendExtra("""
2046set_stage("%(bcb_dev)s", "");
2047endif;
2048endif;
2049""" % bcb_dev)
2050
Michael Runge63f01de2014-10-28 19:24:19 -07002051 if OPTIONS.verify and system_diff:
2052 script.Print("Remounting and verifying system partition files...")
2053 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08002054 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002055 system_diff.EmitExplicitTargetVerification(script)
2056
2057 if OPTIONS.verify and vendor_diff:
2058 script.Print("Remounting and verifying vendor partition files...")
2059 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08002060 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002061 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08002062
2063 # For downgrade OTAs, we prefer to use the update-binary in the source
2064 # build that is actually newer than the one in the target build.
2065 if OPTIONS.downgrade:
2066 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
2067 else:
2068 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07002069
Tao Baod8d14be2016-02-04 14:26:02 -08002070 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07002071 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07002072
2073
2074def main(argv):
2075
2076 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08002077 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002078 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07002079 elif o in ("-k", "--package_key"):
2080 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002081 elif o in ("-i", "--incremental_from"):
2082 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002083 elif o == "--full_radio":
2084 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002085 elif o == "--full_bootloader":
2086 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002087 elif o in ("-w", "--wipe_user_data"):
2088 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002089 elif o == "--downgrade":
2090 OPTIONS.downgrade = True
2091 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002092 elif o == "--override_timestamp":
2093 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07002094 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002095 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002096 elif o == "--oem_no_mount":
2097 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002098 elif o in ("-e", "--extra_script"):
2099 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002100 elif o in ("-t", "--worker_threads"):
2101 if a.isdigit():
2102 OPTIONS.worker_threads = int(a)
2103 else:
2104 raise ValueError("Cannot parse value %r for option %r - only "
2105 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002106 elif o in ("-2", "--two_step"):
2107 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002108 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002109 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002110 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002111 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002112 elif o == "--block":
2113 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002114 elif o in ("-b", "--binary"):
2115 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002116 elif o in ("--no_fallback_to_full",):
2117 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002118 elif o == "--stash_threshold":
2119 try:
2120 OPTIONS.stash_threshold = float(a)
2121 except ValueError:
2122 raise ValueError("Cannot parse value %r for option %r - expecting "
2123 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002124 elif o == "--gen_verify":
2125 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002126 elif o == "--log_diff":
2127 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002128 elif o == "--payload_signer":
2129 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002130 elif o == "--payload_signer_args":
2131 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002132 else:
2133 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002134 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002135
2136 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08002137 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002138 extra_long_opts=[
2139 "board_config=",
2140 "package_key=",
2141 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002142 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002143 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002144 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002145 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002146 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002147 "extra_script=",
2148 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002149 "two_step",
2150 "no_signing",
2151 "block",
2152 "binary=",
2153 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002154 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002155 "verify",
2156 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002157 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002158 "gen_verify",
2159 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002160 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002161 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002162 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002163
2164 if len(args) != 2:
2165 common.Usage(__doc__)
2166 sys.exit(1)
2167
Tao Bao5d182562016-02-23 11:38:39 -08002168 if OPTIONS.downgrade:
2169 # Sanity check to enforce a data wipe.
2170 if not OPTIONS.wipe_user_data:
2171 raise ValueError("Cannot downgrade without a data wipe")
2172
2173 # We should only allow downgrading incrementals (as opposed to full).
2174 # Otherwise the device may go back from arbitrary build with this full
2175 # OTA package.
2176 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002177 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002178
Tao Bao3e6161a2017-02-28 11:48:48 -08002179 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
2180 "Cannot have --downgrade AND --override_timestamp both"
2181
Tao Baoc098e9e2016-01-07 13:03:56 -08002182 # Load the dict file from the zip directly to have a peek at the OTA type.
2183 # For packages using A/B update, unzipping is not needed.
2184 input_zip = zipfile.ZipFile(args[0], "r")
2185 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2186 common.ZipClose(input_zip)
2187
2188 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2189
2190 if ab_update:
2191 if OPTIONS.incremental_source is not None:
2192 OPTIONS.target_info_dict = OPTIONS.info_dict
2193 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2194 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2195 common.ZipClose(source_zip)
2196
2197 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002198 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002199 common.DumpInfoDict(OPTIONS.info_dict)
2200
2201 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002202 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002203 common.DumpInfoDict(OPTIONS.source_info_dict)
2204
2205 WriteABOTAPackageWithBrilloScript(
2206 target_file=args[0],
2207 output_file=args[1],
2208 source_file=OPTIONS.incremental_source)
2209
Tao Bao89fbb0f2017-01-10 10:47:58 -08002210 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002211 return
2212
Doug Zongker1c390a22009-05-14 19:06:36 -07002213 if OPTIONS.extra_script is not None:
2214 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2215
Tao Bao89fbb0f2017-01-10 10:47:58 -08002216 print("unzipping target target-files...")
Doug Zongker55d93282011-01-25 17:03:34 -08002217 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002218
Doug Zongkereef39442009-04-02 12:14:19 -07002219 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002220 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002221
Doug Zongker37974732010-09-16 17:44:38 -07002222 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002223 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07002224 common.DumpInfoDict(OPTIONS.info_dict)
2225
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002226 # If the caller explicitly specified the device-specific extensions
2227 # path via -s/--device_specific, use that. Otherwise, use
2228 # META/releasetools.py if it is present in the target target_files.
2229 # Otherwise, take the path of the file from 'tool_extensions' in the
2230 # info dict and look for that in the local filesystem, relative to
2231 # the current directory.
2232
Doug Zongker37974732010-09-16 17:44:38 -07002233 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002234 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2235 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08002236 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002237 OPTIONS.device_specific = from_input
2238 else:
2239 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2240
Doug Zongker37974732010-09-16 17:44:38 -07002241 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002242 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002243
Tao Baoc098e9e2016-01-07 13:03:56 -08002244 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002245 raise common.ExternalError(
2246 "--- target build has specified no recovery ---")
2247
Tao Bao767e3ac2015-11-10 12:19:19 -08002248 # Use the default key to sign the package if not specified with package_key.
2249 if not OPTIONS.no_signing:
2250 if OPTIONS.package_key is None:
2251 OPTIONS.package_key = OPTIONS.info_dict.get(
2252 "default_system_dev_certificate",
2253 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002254
Tao Bao767e3ac2015-11-10 12:19:19 -08002255 # Set up the output zip. Create a temporary zip file if signing is needed.
2256 if OPTIONS.no_signing:
2257 if os.path.exists(args[1]):
2258 os.unlink(args[1])
2259 output_zip = zipfile.ZipFile(args[1], "w",
2260 compression=zipfile.ZIP_DEFLATED)
2261 else:
2262 temp_zip_file = tempfile.NamedTemporaryFile()
2263 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2264 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002265
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002266 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002267 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002268 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002269 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002270 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002271
Tao Bao9bc6bb22015-11-09 16:58:28 -08002272 # Generate a verify package.
2273 if OPTIONS.gen_verify:
2274 WriteVerifyPackage(input_zip, output_zip)
2275
Tao Bao767e3ac2015-11-10 12:19:19 -08002276 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002277 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002278 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002279
2280 # Generate an incremental OTA. It will fall back to generate a full OTA on
2281 # failure unless no_fallback_to_full is specified.
2282 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002283 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08002284 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2285 OPTIONS.incremental_source)
2286 OPTIONS.target_info_dict = OPTIONS.info_dict
2287 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2288 OPTIONS.source_tmp)
2289 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002290 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002291 common.DumpInfoDict(OPTIONS.source_info_dict)
2292 try:
2293 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002294 if OPTIONS.log_diff:
2295 out_file = open(OPTIONS.log_diff, 'w')
2296 import target_files_diff
2297 target_files_diff.recursiveDiff('',
2298 OPTIONS.source_tmp,
2299 OPTIONS.input_tmp,
2300 out_file)
2301 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002302 except ValueError:
2303 if not OPTIONS.fallback_to_full:
2304 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08002305 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002306 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002307 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002308
Tao Bao767e3ac2015-11-10 12:19:19 -08002309 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002310
Tao Bao767e3ac2015-11-10 12:19:19 -08002311 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002312 if not OPTIONS.no_signing:
2313 SignOutput(temp_zip_file.name, args[1])
2314 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002315
Tao Bao89fbb0f2017-01-10 10:47:58 -08002316 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002317
2318
2319if __name__ == '__main__':
2320 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002321 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002322 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002323 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002324 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002325 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002326 finally:
2327 common.Cleanup()