blob: 0f129820c964e36a34e2312db5b3995209afe0a7 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given a target-files zipfile, produces an OTA package that installs
19that build. An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage: ota_from_target_files [flags] input_target_files output_ota_package
23
Doug Zongker25568482014-03-03 10:21:27 -080024 --board_config <file>
Doug Zongkerfdd8e692009-08-03 17:27:48 -070025 Deprecated.
Doug Zongkereef39442009-04-02 12:14:19 -070026
Doug Zongkerafb32ea2011-09-22 10:28:04 -070027 -k (--package_key) <key> Key to use to sign the package (default is
28 the value of default_system_dev_certificate from the input
29 target-files's META/misc_info.txt, or
30 "build/target/product/security/testkey" if that value is not
31 specified).
32
33 For incremental OTAs, the default value is based on the source
34 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070035
36 -i (--incremental_from) <file>
37 Generate an incremental OTA using the given target-files zip as
38 the starting build.
39
Tao Bao43078aa2015-04-21 14:32:35 -070040 --full_radio
41 When generating an incremental OTA, always include a full copy of
42 radio image. This option is only meaningful when -i is specified,
43 because a full radio is always included in a full OTA if applicable.
44
leozwangaa6c1a12015-08-14 10:57:58 -070045 --full_bootloader
46 Similar to --full_radio. When generating an incremental OTA, always
47 include a full copy of bootloader image.
48
Michael Runge63f01de2014-10-28 19:24:19 -070049 -v (--verify)
50 Remount and verify the checksums of the files written to the
51 system and vendor (if used) partitions. Incremental builds only.
52
Michael Runge6e836112014-04-15 17:40:21 -070053 -o (--oem_settings) <file>
54 Use the file to specify the expected OEM-specific properties
55 on the OEM partition of the intended device.
56
Tao Bao8608cde2016-02-25 19:49:55 -080057 --oem_no_mount
58 For devices with OEM-specific properties but without an OEM partition,
59 do not mount the OEM partition in the updater-script. This should be
60 very rarely used, since it's expected to have a dedicated OEM partition
61 for OEM-specific properties. Only meaningful when -o is specified.
62
Doug Zongkerdbfaae52009-04-21 17:12:54 -070063 -w (--wipe_user_data)
64 Generate an OTA package that will wipe the user data partition
65 when installed.
66
Tao Bao5d182562016-02-23 11:38:39 -080067 --downgrade
68 Intentionally generate an incremental OTA that updates from a newer
69 build to an older one (based on timestamp comparison). "post-timestamp"
70 will be replaced by "ota-downgrade=yes" in the metadata file. A data
71 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080072 the metadata file. The update-binary in the source build will be used in
73 the OTA package, unless --binary flag is specified.
Tao Bao5d182562016-02-23 11:38:39 -080074
Doug Zongker1c390a22009-05-14 19:06:36 -070075 -e (--extra_script) <file>
76 Insert the contents of file at the end of the update script.
77
Doug Zongker9b23f2c2013-11-25 14:44:12 -080078 -2 (--two_step)
79 Generate a 'two-step' OTA package, where recovery is updated
80 first, so that any changes made to the system partition are done
81 using the new recovery (new kernel, etc.).
82
Doug Zongker26e66192014-02-20 13:22:07 -080083 --block
84 Generate a block-based OTA if possible. Will fall back to a
85 file-based OTA if the target_files is older and doesn't support
86 block-based OTAs.
87
Doug Zongker25568482014-03-03 10:21:27 -080088 -b (--binary) <file>
89 Use the given binary as the update-binary in the output package,
90 instead of the binary in the build's target_files. Use for
91 development only.
92
Martin Blumenstingl374e1142014-05-31 20:42:55 +020093 -t (--worker_threads) <int>
94 Specifies the number of worker-threads that will be used when
95 generating patches for incremental updates (defaults to 3).
96
Tao Bao8dcf7382015-05-21 14:09:49 -070097 --stash_threshold <float>
98 Specifies the threshold that will be used to compute the maximum
99 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800100
101 --gen_verify
102 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800103
104 --log_diff <file>
105 Generate a log file that shows the differences in the source and target
106 builds for an incremental package. This option is only meaningful when
107 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700108
109 --payload_signer <signer>
110 Specify the signer when signing the payload and metadata for A/B OTAs.
111 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
112 with the package private key. If the private key cannot be accessed
113 directly, a payload signer that knows how to do that should be specified.
114 The signer will be supplied with "-inkey <path_to_key>",
115 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700116
117 --payload_signer_args <args>
118 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700119"""
120
121import sys
122
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800123if sys.hexversion < 0x02070000:
124 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -0700125 sys.exit(1)
126
Doug Zongkerfc44a512014-08-26 13:10:25 -0700127import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700128import os
Tao Baoc098e9e2016-01-07 13:03:56 -0800129import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700130import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700131import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700132import zipfile
133
134import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700135import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700136import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700137
138OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700139OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700140OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700141OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700142OPTIONS.require_verbatim = set()
143OPTIONS.prohibit_verbatim = set(("system/build.prop",))
144OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700145OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800146OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700147OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700148OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
149if OPTIONS.worker_threads == 0:
150 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800151OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900152OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800153OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800154OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700155OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800156OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700157OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700158OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700159OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700160# Stash size cannot exceed cache_size * threshold.
161OPTIONS.cache_size = None
162OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800163OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800164OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700165OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700166OPTIONS.payload_signer_args = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700167
Doug Zongkereef39442009-04-02 12:14:19 -0700168def MostPopularKey(d, default):
169 """Given a dict, return the key corresponding to the largest
170 value. Returns 'default' if the dict is empty."""
171 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700172 if not x:
173 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700174 x.sort()
175 return x[-1][1]
176
177
178def IsSymlink(info):
179 """Return true if the zipfile.ZipInfo object passed in represents a
180 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700181 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700182
Hristo Bojinov96be7202010-08-02 10:26:17 -0700183def IsRegular(info):
184 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700185 regular file."""
186 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700187
Michael Runge4038aa82013-12-13 18:06:28 -0800188def ClosestFileMatch(src, tgtfiles, existing):
189 """Returns the closest file match between a source file and list
190 of potential matches. The exact filename match is preferred,
191 then the sha1 is searched for, and finally a file with the same
192 basename is evaluated. Rename support in the updater-binary is
193 required for the latter checks to be used."""
194
195 result = tgtfiles.get("path:" + src.name)
196 if result is not None:
197 return result
198
199 if not OPTIONS.target_info_dict.get("update_rename_support", False):
200 return None
201
202 if src.size < 1000:
203 return None
204
205 result = tgtfiles.get("sha1:" + src.sha1)
206 if result is not None and existing.get(result.name) is None:
207 return result
208 result = tgtfiles.get("file:" + src.name.split("/")[-1])
209 if result is not None and existing.get(result.name) is None:
210 return result
211 return None
212
Dan Albert8b72aef2015-03-23 19:13:21 -0700213class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700214 def __init__(self, partition, fs_config):
215 self.partition = partition
216 self.fs_config = fs_config
217 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700218
Dan Albert8b72aef2015-03-23 19:13:21 -0700219 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700220 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700221 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700222 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700223
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700224 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700225 # The target_files contains a record of what the uid,
226 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700227 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700228
229 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700230 if not line:
231 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700232 columns = line.split()
233 name, uid, gid, mode = columns[:4]
234 selabel = None
235 capabilities = None
236
237 # After the first 4 columns, there are a series of key=value
238 # pairs. Extract out the fields we care about.
239 for element in columns[4:]:
240 key, value = element.split("=")
241 if key == "selabel":
242 selabel = value
243 if key == "capabilities":
244 capabilities = value
245
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700246 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700247 if i is not None:
248 i.uid = int(uid)
249 i.gid = int(gid)
250 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700251 i.selabel = selabel
252 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700253 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700254 i.children.sort(key=lambda i: i.name)
255
Tao Baof2cffbd2015-07-22 12:33:18 -0700256 # Set metadata for the files generated by this script. For full recovery
257 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700258 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700259 if i:
260 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700261 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700262 if i:
263 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700264
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700265
Dan Albert8b72aef2015-03-23 19:13:21 -0700266class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700267 """Items represent the metadata (user, group, mode) of files and
268 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700269 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700270 self.itemset = itemset
271 self.name = name
272 self.uid = None
273 self.gid = None
274 self.mode = None
275 self.selabel = None
276 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700277 self.is_dir = is_dir
278 self.descendants = None
279 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700280
281 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700282 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700283 self.parent.children.append(self)
284 else:
285 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700286 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700287 self.children = []
288
289 def Dump(self, indent=0):
290 if self.uid is not None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700291 print "%s%s %d %d %o" % (
292 " " * indent, self.name, self.uid, self.gid, self.mode)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700293 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700294 print "%s%s %s %s %s" % (
295 " " * indent, self.name, self.uid, self.gid, self.mode)
296 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700297 print "%s%s" % (" "*indent, self.descendants)
298 print "%s%s" % (" "*indent, self.best_subtree)
299 for i in self.children:
300 i.Dump(indent=indent+1)
301
Doug Zongkereef39442009-04-02 12:14:19 -0700302 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700303 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700304 all children and determine the best strategy for using set_perm_recursive
305 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700306 values. Recursively calls itself for all descendants.
307
Dan Albert8b72aef2015-03-23 19:13:21 -0700308 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
309 counting up all descendants of this node. (dmode or fmode may be None.)
310 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
311 fmode, selabel, capabilities) tuple that will match the most descendants of
312 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700313 """
314
Dan Albert8b72aef2015-03-23 19:13:21 -0700315 assert self.is_dir
316 key = (self.uid, self.gid, self.mode, None, self.selabel,
317 self.capabilities)
318 self.descendants = {key: 1}
319 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700320 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700321 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700322 for k, v in i.CountChildMetadata().iteritems():
323 d[k] = d.get(k, 0) + v
324 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700325 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700326 d[k] = d.get(k, 0) + 1
327
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700328 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
329 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700330
331 # First, find the (uid, gid) pair that matches the most
332 # descendants.
333 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700334 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700335 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
336 ug = MostPopularKey(ug, (0, 0))
337
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700338 # Now find the dmode, fmode, selabel, and capabilities that match
339 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700340 best_dmode = (0, 0o755)
341 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700342 best_selabel = (0, None)
343 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700344 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700345 if k[:2] != ug:
346 continue
347 if k[2] is not None and count >= best_dmode[0]:
348 best_dmode = (count, k[2])
349 if k[3] is not None and count >= best_fmode[0]:
350 best_fmode = (count, k[3])
351 if k[4] is not None and count >= best_selabel[0]:
352 best_selabel = (count, k[4])
353 if k[5] is not None and count >= best_capabilities[0]:
354 best_capabilities = (count, k[5])
355 self.best_subtree = ug + (
356 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700357
358 return d
359
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700360 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700361 """Append set_perm/set_perm_recursive commands to 'script' to
362 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700363 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700364
365 self.CountChildMetadata()
366
367 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700368 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
369 # that the current item (and all its children) have already been set to.
370 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700371 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700372 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700373 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700374 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700375 current = item.best_subtree
376
377 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700378 item.mode != current[2] or item.selabel != current[4] or \
379 item.capabilities != current[5]:
380 script.SetPermissions("/"+item.name, item.uid, item.gid,
381 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700382
383 for i in item.children:
384 recurse(i, current)
385 else:
386 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700387 item.mode != current[3] or item.selabel != current[4] or \
388 item.capabilities != current[5]:
389 script.SetPermissions("/"+item.name, item.uid, item.gid,
390 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700391
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700392 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700393
394
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700395def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
396 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700397 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800398 list of symlinks. output_zip may be None, in which case the copy is
399 skipped (but the other side effects still happen). substitute is an
400 optional dict of {output filename: contents} to be output instead of
401 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700402 """
403
404 symlinks = []
405
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700406 partition = itemset.partition
407
Doug Zongkereef39442009-04-02 12:14:19 -0700408 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700409 prefix = partition.upper() + "/"
410 if info.filename.startswith(prefix):
411 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700412 if IsSymlink(info):
413 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700414 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700415 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700416 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700417 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700418 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700419 if substitute and fn in substitute and substitute[fn] is None:
420 continue
421 if output_zip is not None:
422 if substitute and fn in substitute:
423 data = substitute[fn]
424 else:
425 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700426 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700427 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700428 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700429 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700430 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700431
432 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800433 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700434
435
Doug Zongkereef39442009-04-02 12:14:19 -0700436def SignOutput(temp_zip_name, output_zip_name):
437 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
438 pw = key_passwords[OPTIONS.package_key]
439
Doug Zongker951495f2009-08-14 12:44:19 -0700440 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
441 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700442
443
Dan Albert8b72aef2015-03-23 19:13:21 -0700444def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700445 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700446 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700447 device = GetBuildProp("ro.product.device", info_dict)
448 script.AssertDevice(device)
449 else:
450 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700451 raise common.ExternalError(
452 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700453 for prop in oem_props.split():
454 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700455 raise common.ExternalError(
456 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700457 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700458
Doug Zongkereef39442009-04-02 12:14:19 -0700459
Tao Baod42e97e2016-11-30 12:11:57 -0800460def _WriteRecoveryImageToBoot(script, output_zip):
461 """Find and write recovery image to /boot in two-step OTA.
462
463 In two-step OTAs, we write recovery image to /boot as the first step so that
464 we can reboot to there and install a new recovery image to /recovery.
465 A special "recovery-two-step.img" will be preferred, which encodes the correct
466 path of "/boot". Otherwise the device may show "device is corrupt" message
467 when booting into /boot.
468
469 Fall back to using the regular recovery.img if the two-step recovery image
470 doesn't exist. Note that rebuilding the special image at this point may be
471 infeasible, because we don't have the desired boot signer and keys when
472 calling ota_from_target_files.py.
473 """
474
475 recovery_two_step_img_name = "recovery-two-step.img"
476 recovery_two_step_img_path = os.path.join(
477 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
478 if os.path.exists(recovery_two_step_img_path):
479 recovery_two_step_img = common.GetBootableImage(
480 recovery_two_step_img_name, recovery_two_step_img_name,
481 OPTIONS.input_tmp, "RECOVERY")
482 common.ZipWriteStr(
483 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
484 print "two-step package: using %s in stage 1/3" % (
485 recovery_two_step_img_name,)
486 script.WriteRawImage("/boot", recovery_two_step_img_name)
487 else:
488 print "two-step package: using recovery.img in stage 1/3"
489 # The "recovery.img" entry has been written into package earlier.
490 script.WriteRawImage("/boot", "recovery.img")
491
492
Doug Zongkerc9253822014-02-04 12:17:58 -0800493def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700494 namelist = [name for name in target_files_zip.namelist()]
495 return ("SYSTEM/recovery-from-boot.p" in namelist or
496 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700497
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700498def HasVendorPartition(target_files_zip):
499 try:
500 target_files_zip.getinfo("VENDOR/")
501 return True
502 except KeyError:
503 return False
504
Michael Runge6e836112014-04-15 17:40:21 -0700505def GetOemProperty(name, oem_props, oem_dict, info_dict):
506 if oem_props is not None and name in oem_props:
507 return oem_dict[name]
508 return GetBuildProp(name, info_dict)
509
510
511def CalculateFingerprint(oem_props, oem_dict, info_dict):
512 if oem_props is None:
513 return GetBuildProp("ro.build.fingerprint", info_dict)
514 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700515 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
516 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
517 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
518 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700519
Doug Zongkerfc44a512014-08-26 13:10:25 -0700520
Doug Zongker3c84f562014-07-31 11:06:30 -0700521def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700522 # Return an image object (suitable for passing to BlockImageDiff)
523 # for the 'which' partition (most be "system" or "vendor"). If a
524 # prebuilt image and file map are found in tmpdir they are used,
525 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700526
527 assert which in ("system", "vendor")
528
529 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700530 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
531 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700532 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700533 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700534
535 else:
536 print "building %s.img from target-files" % (which,)
537
538 # This is an 'old' target-files, which does not contain images
539 # already built. Build them.
540
Doug Zongkerfc44a512014-08-26 13:10:25 -0700541 mappath = tempfile.mkstemp()[1]
542 OPTIONS.tempfiles.append(mappath)
543
Doug Zongker3c84f562014-07-31 11:06:30 -0700544 import add_img_to_target_files
545 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700546 path = add_img_to_target_files.BuildSystem(
547 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700548 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700549 path = add_img_to_target_files.BuildVendor(
550 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700551
Tao Baoff777812015-05-12 11:42:31 -0700552 # Bug: http://b/20939131
553 # In ext4 filesystems, block 0 might be changed even being mounted
554 # R/O. We add it to clobbered_blocks so that it will be written to the
555 # target unconditionally. Note that they are still part of care_map.
556 clobbered_blocks = "0"
557
558 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700559
560
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700561def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700562 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700563 # be installed on top of. For now, we expect the API just won't
564 # change very often. Similarly for fstab, it might have changed
565 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700566 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700567
Tao Bao838c68f2016-03-15 19:16:18 +0000568 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700569 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -0700570 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700571 if oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700572 if OPTIONS.oem_source is None:
573 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800574 if not OPTIONS.oem_no_mount:
575 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700576 oem_dict = common.LoadDictionaryFromLines(
577 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700578
Tao Bao3e30d972016-03-15 13:20:19 -0700579 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700580 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700581 "post-build": target_fp,
Dan Albert8b72aef2015-03-23 19:13:21 -0700582 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
583 OPTIONS.info_dict),
584 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
585 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700586
Doug Zongker05d3dea2009-06-22 11:32:31 -0700587 device_specific = common.DeviceSpecificParams(
588 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700589 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700590 output_zip=output_zip,
591 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700592 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700593 metadata=metadata,
594 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700595
Doug Zongkerc9253822014-02-04 12:17:58 -0800596 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800597 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800598
Tao Baod8d14be2016-02-04 14:26:02 -0800599 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
600
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700601 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
602 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
603 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700604
Michael Runge6e836112014-04-15 17:40:21 -0700605 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700606 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800607
608 # Two-step package strategy (in chronological order, which is *not*
609 # the order in which the generated script has things):
610 #
611 # if stage is not "2/3" or "3/3":
612 # write recovery image to boot partition
613 # set stage to "2/3"
614 # reboot to boot partition and restart recovery
615 # else if stage is "2/3":
616 # write recovery image to recovery partition
617 # set stage to "3/3"
618 # reboot to recovery partition and restart recovery
619 # else:
620 # (stage must be "3/3")
621 # set stage to ""
622 # do normal full package installation:
623 # wipe and install system, boot image, etc.
624 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700625 # complete script normally
626 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800627
628 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
629 OPTIONS.input_tmp, "RECOVERY")
630 if OPTIONS.two_step:
631 if not OPTIONS.info_dict.get("multistage_support", None):
632 assert False, "two-step packages not supported by this build"
633 fs = OPTIONS.info_dict["fstab"]["/misc"]
634 assert fs.fs_type.upper() == "EMMC", \
635 "two-step packages only supported on devices with EMMC /misc partitions"
636 bcb_dev = {"bcb_dev": fs.device}
637 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
638 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700639if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800640""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800641
642 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
643 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800644 script.WriteRawImage("/recovery", "recovery.img")
645 script.AppendExtra("""
646set_stage("%(bcb_dev)s", "3/3");
647reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700648else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800649""" % bcb_dev)
650
Tao Baod42e97e2016-11-30 12:11:57 -0800651 # Stage 3/3: Make changes.
652 script.Comment("Stage 3/3")
653
Tao Bao6c55a8a2015-04-08 15:30:27 -0700654 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700655 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700656
Doug Zongkere5ff5902012-01-17 10:55:37 -0800657 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700658
Doug Zongker01ce19c2014-02-04 13:48:15 -0800659 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700660
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700661 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800662 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700663 if HasVendorPartition(input_zip):
664 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700665
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400666 # Place a copy of file_contexts.bin into the OTA package which will be used
667 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700668 if "selinux_fc" in OPTIONS.info_dict:
669 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500670
Michael Runge7cd99ba2014-10-22 17:21:48 -0700671 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
672
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700673 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700674 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800675
Doug Zongker26e66192014-02-20 13:22:07 -0800676 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700677 # Full OTA is done as an "incremental" against an empty source
678 # image. This has the effect of writing new data from the package
679 # to the entire partition, but lets us reuse the updater code that
680 # writes incrementals to do it.
681 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
682 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700683 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700684 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800685 else:
686 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700687 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800688 if not has_recovery_patch:
689 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800690 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700691
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700692 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800693 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700694
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700695 boot_img = common.GetBootableImage(
696 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800697
Doug Zongker91a99c22014-05-09 13:15:01 -0700698 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800699 def output_sink(fn, data):
700 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700701 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800702
703 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
704 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700705
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700706 system_items.GetMetadata(input_zip)
707 system_items.Get("system").SetPermissions(script)
708
709 if HasVendorPartition(input_zip):
710 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
711 script.ShowProgress(0.1, 0)
712
713 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700714 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
715 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700716 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700717 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700718 else:
719 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700720 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700721 script.UnpackPackageDir("vendor", "/vendor")
722
723 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
724 script.MakeSymlinks(symlinks)
725
726 vendor_items.GetMetadata(input_zip)
727 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700728
Doug Zongker37974732010-09-16 17:44:38 -0700729 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700730 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700731
Doug Zongker01ce19c2014-02-04 13:48:15 -0800732 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700733 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700734
Doug Zongker01ce19c2014-02-04 13:48:15 -0800735 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700736 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700737
Doug Zongker1c390a22009-05-14 19:06:36 -0700738 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700739 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700740
Doug Zongker14833602010-02-02 13:12:04 -0800741 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800742
Doug Zongker922206e2014-03-04 13:16:24 -0800743 if OPTIONS.wipe_user_data:
744 script.ShowProgress(0.1, 10)
745 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700746
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800747 if OPTIONS.two_step:
748 script.AppendExtra("""
749set_stage("%(bcb_dev)s", "");
750""" % bcb_dev)
751 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800752
753 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
754 script.Comment("Stage 1/3")
755 _WriteRecoveryImageToBoot(script, output_zip)
756
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800757 script.AppendExtra("""
758set_stage("%(bcb_dev)s", "2/3");
759reboot_now("%(bcb_dev)s", "");
760endif;
761endif;
762""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800763
Tao Bao5d182562016-02-23 11:38:39 -0800764 script.SetProgress(1)
765 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800766 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700767 WriteMetadata(metadata, output_zip)
768
Doug Zongkerfc44a512014-08-26 13:10:25 -0700769
Dan Albert8e0178d2015-01-27 15:53:15 -0800770def WritePolicyConfig(file_name, output_zip):
771 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500772
Doug Zongker2ea21062010-04-28 16:05:21 -0700773
774def WriteMetadata(metadata, output_zip):
775 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
776 "".join(["%s=%s\n" % kv
777 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700778
Doug Zongkerfc44a512014-08-26 13:10:25 -0700779
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700780def LoadPartitionFiles(z, partition):
781 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700782 ZipFile, and return a dict of {filename: File object}."""
783 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700784 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700785 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700786 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700787 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700788 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700789 data = z.read(info.filename)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +0900790 out[fn] = common.File(fn, data, info.compress_size)
Doug Zongker1807e702012-02-28 12:21:08 -0800791 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700792
793
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700794def GetBuildProp(prop, info_dict):
795 """Return the fingerprint of the build of a given target-files info_dict."""
796 try:
797 return info_dict.get("build.prop", {})[prop]
798 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700799 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700800
Doug Zongkerfc44a512014-08-26 13:10:25 -0700801
Michael Runge4038aa82013-12-13 18:06:28 -0800802def AddToKnownPaths(filename, known_paths):
803 if filename[-1] == "/":
804 return
805 dirs = filename.split("/")[:-1]
806 while len(dirs) > 0:
807 path = "/".join(dirs)
808 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700809 break
Michael Runge4038aa82013-12-13 18:06:28 -0800810 known_paths.add(path)
811 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700812
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700813
Geremy Condra36bd3652014-02-06 19:45:10 -0800814def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700815 # TODO(tbao): We should factor out the common parts between
816 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800817 source_version = OPTIONS.source_info_dict["recovery_api_version"]
818 target_version = OPTIONS.target_info_dict["recovery_api_version"]
819
820 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700821 print("WARNING: generating edify script for a source that "
822 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700823 script = edify_generator.EdifyGenerator(
824 source_version, OPTIONS.target_info_dict,
825 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800826
Tao Bao3806c232015-07-05 21:08:33 -0700827 recovery_mount_options = OPTIONS.source_info_dict.get(
828 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700829 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
830 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Tao Bao3806c232015-07-05 21:08:33 -0700831 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700832 if source_oem_props or target_oem_props:
Tao Bao3806c232015-07-05 21:08:33 -0700833 if OPTIONS.oem_source is None:
834 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800835 if not OPTIONS.oem_no_mount:
836 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700837 oem_dict = common.LoadDictionaryFromLines(
838 open(OPTIONS.oem_source).readlines())
839
Dan Albert8b72aef2015-03-23 19:13:21 -0700840 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700841 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
842 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800843 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700844 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800845
Tao Bao5d182562016-02-23 11:38:39 -0800846 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
847 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
848 is_downgrade = long(post_timestamp) < long(pre_timestamp)
849
850 if OPTIONS.downgrade:
851 metadata["ota-downgrade"] = "yes"
852 if not is_downgrade:
853 raise RuntimeError("--downgrade specified but no downgrade detected: "
854 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
855 else:
856 if is_downgrade:
857 # Non-fatal here to allow generating such a package which may require
858 # manual work to adjust the post-timestamp. A legit use case is that we
859 # cut a new build C (after having A and B), but want to enfore the
860 # update path of A -> C -> B. Specifying --downgrade may not help since
861 # that would enforce a data wipe for C -> B update.
862 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
863 "The package may not be deployed properly. "
864 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
865 metadata["post-timestamp"] = post_timestamp
866
Geremy Condra36bd3652014-02-06 19:45:10 -0800867 device_specific = common.DeviceSpecificParams(
868 source_zip=source_zip,
869 source_version=source_version,
870 target_zip=target_zip,
871 target_version=target_version,
872 output_zip=output_zip,
873 script=script,
874 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700875 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800876
Tao Bao3e30d972016-03-15 13:20:19 -0700877 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700878 OPTIONS.source_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -0700879 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700880 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800881 metadata["pre-build"] = source_fp
882 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700883 metadata["pre-build-incremental"] = GetBuildProp(
884 "ro.build.version.incremental", OPTIONS.source_info_dict)
885 metadata["post-build-incremental"] = GetBuildProp(
886 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800887
888 source_boot = common.GetBootableImage(
889 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
890 OPTIONS.source_info_dict)
891 target_boot = common.GetBootableImage(
892 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
893 updating_boot = (not OPTIONS.two_step and
894 (source_boot.data != target_boot.data))
895
Geremy Condra36bd3652014-02-06 19:45:10 -0800896 target_recovery = common.GetBootableImage(
897 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800898
Doug Zongkerfc44a512014-08-26 13:10:25 -0700899 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
900 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700901
902 blockimgdiff_version = 1
903 if OPTIONS.info_dict:
904 blockimgdiff_version = max(
905 int(i) for i in
906 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
907
Tao Baof8acad12016-07-07 09:09:58 -0700908 # Check the first block of the source system partition for remount R/W only
909 # if the filesystem is ext4.
910 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
911 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700912 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
913 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
914 # b) the blocks listed in block map may not contain all the bytes for a given
915 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700916 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
917 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
918 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700919 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800920 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700921 version=blockimgdiff_version,
922 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700923
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700924 if HasVendorPartition(target_zip):
925 if not HasVendorPartition(source_zip):
926 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700927 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
928 OPTIONS.source_info_dict)
929 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
930 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800931
932 # Check first block of vendor partition for remount R/W only if
933 # disk type is ext4
934 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800935 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700936 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700937 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800938 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700939 version=blockimgdiff_version,
940 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700941 else:
942 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800943
Michael Rungec6e3afd2014-05-05 11:55:47 -0700944 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800945 device_specific.IncrementalOTA_Assertions()
946
947 # Two-step incremental package strategy (in chronological order,
948 # which is *not* the order in which the generated script has
949 # things):
950 #
951 # if stage is not "2/3" or "3/3":
952 # do verification on current system
953 # write recovery image to boot partition
954 # set stage to "2/3"
955 # reboot to boot partition and restart recovery
956 # else if stage is "2/3":
957 # write recovery image to recovery partition
958 # set stage to "3/3"
959 # reboot to recovery partition and restart recovery
960 # else:
961 # (stage must be "3/3")
962 # perform update:
963 # patch system files, etc.
964 # force full install of new boot image
965 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700966 # complete script normally
967 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800968
969 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700970 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800971 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700972 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800973 assert fs.fs_type.upper() == "EMMC", \
974 "two-step packages only supported on devices with EMMC /misc partitions"
975 bcb_dev = {"bcb_dev": fs.device}
976 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
977 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700978if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800979""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800980
981 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
982 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700983 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800984 script.WriteRawImage("/recovery", "recovery.img")
985 script.AppendExtra("""
986set_stage("%(bcb_dev)s", "3/3");
987reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700988else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800989""" % bcb_dev)
990
Tao Baod42e97e2016-11-30 12:11:57 -0800991 # Stage 1/3: (a) Verify the current system.
992 script.Comment("Stage 1/3")
993
Tao Bao6c55a8a2015-04-08 15:30:27 -0700994 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -0800995 script.Print("Source: %s" % (source_fp,))
996 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700997
Geremy Condra36bd3652014-02-06 19:45:10 -0800998 script.Print("Verifying current system...")
999
1000 device_specific.IncrementalOTA_VerifyBegin()
1001
Tao Bao3e30d972016-03-15 13:20:19 -07001002 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1003 # patching on a device that's already on the target build will damage the
1004 # system. Because operations like move don't check the block state, they
1005 # always apply the changes unconditionally.
1006 if blockimgdiff_version <= 2:
1007 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001008 script.AssertSomeFingerprint(source_fp)
1009 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001010 script.AssertSomeThumbprint(
1011 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001012
1013 else: # blockimgdiff_version > 2
1014 if source_oem_props is None and target_oem_props is None:
1015 script.AssertSomeFingerprint(source_fp, target_fp)
1016 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001017 script.AssertSomeThumbprint(
1018 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1019 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001020 elif source_oem_props is None and target_oem_props is not None:
1021 script.AssertFingerprintOrThumbprint(
1022 source_fp,
1023 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1024 else:
1025 script.AssertFingerprintOrThumbprint(
1026 target_fp,
1027 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001028
Tao Baod8d14be2016-02-04 14:26:02 -08001029 # Check the required cache size (i.e. stashed blocks).
1030 size = []
1031 if system_diff:
1032 size.append(system_diff.required_cache)
1033 if vendor_diff:
1034 size.append(vendor_diff.required_cache)
1035
Geremy Condra36bd3652014-02-06 19:45:10 -08001036 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001037 boot_type, boot_device = common.GetTypeAndDevice(
1038 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001039 d = common.Difference(target_boot, source_boot)
1040 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001041 if d is None:
1042 include_full_boot = True
1043 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1044 else:
1045 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001046
Doug Zongkerf8340082014-08-05 10:39:37 -07001047 print "boot target: %d source: %d diff: %d" % (
1048 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001049
Doug Zongkerf8340082014-08-05 10:39:37 -07001050 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001051
Doug Zongkerf8340082014-08-05 10:39:37 -07001052 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1053 (boot_type, boot_device,
1054 source_boot.size, source_boot.sha1,
1055 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001056 size.append(target_boot.size)
1057
1058 if size:
1059 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001060
1061 device_specific.IncrementalOTA_VerifyEnd()
1062
1063 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001064 # Stage 1/3: (b) Write recovery image to /boot.
1065 _WriteRecoveryImageToBoot(script, output_zip)
1066
Geremy Condra36bd3652014-02-06 19:45:10 -08001067 script.AppendExtra("""
1068set_stage("%(bcb_dev)s", "2/3");
1069reboot_now("%(bcb_dev)s", "");
1070else
1071""" % bcb_dev)
1072
Tao Baod42e97e2016-11-30 12:11:57 -08001073 # Stage 3/3: Make changes.
1074 script.Comment("Stage 3/3")
1075
Jesse Zhao75bcea02015-01-06 10:59:53 -08001076 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001077 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001078 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001079 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001080
Geremy Condra36bd3652014-02-06 19:45:10 -08001081 script.Comment("---- start making changes here ----")
1082
1083 device_specific.IncrementalOTA_InstallBegin()
1084
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001085 system_diff.WriteScript(script, output_zip,
1086 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001087
Doug Zongkerfc44a512014-08-26 13:10:25 -07001088 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001089 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001090
1091 if OPTIONS.two_step:
1092 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1093 script.WriteRawImage("/boot", "boot.img")
1094 print "writing full boot image (forced by two-step mode)"
1095
1096 if not OPTIONS.two_step:
1097 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001098 if include_full_boot:
1099 print "boot image changed; including full."
1100 script.Print("Installing boot image...")
1101 script.WriteRawImage("/boot", "boot.img")
1102 else:
1103 # Produce the boot image by applying a patch to the current
1104 # contents of the boot partition, and write it back to the
1105 # partition.
1106 print "boot image changed; including patch."
1107 script.Print("Patching boot image...")
1108 script.ShowProgress(0.1, 10)
1109 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1110 % (boot_type, boot_device,
1111 source_boot.size, source_boot.sha1,
1112 target_boot.size, target_boot.sha1),
1113 "-",
1114 target_boot.size, target_boot.sha1,
1115 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001116 else:
1117 print "boot image unchanged; skipping."
1118
1119 # Do device-specific installation (eg, write radio image).
1120 device_specific.IncrementalOTA_InstallEnd()
1121
1122 if OPTIONS.extra_script is not None:
1123 script.AppendExtra(OPTIONS.extra_script)
1124
Doug Zongker922206e2014-03-04 13:16:24 -08001125 if OPTIONS.wipe_user_data:
1126 script.Print("Erasing user data...")
1127 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001128 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001129
Geremy Condra36bd3652014-02-06 19:45:10 -08001130 if OPTIONS.two_step:
1131 script.AppendExtra("""
1132set_stage("%(bcb_dev)s", "");
1133endif;
1134endif;
1135""" % bcb_dev)
1136
1137 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001138 # For downgrade OTAs, we prefer to use the update-binary in the source
1139 # build that is actually newer than the one in the target build.
1140 if OPTIONS.downgrade:
1141 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1142 else:
1143 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001144 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001145 WriteMetadata(metadata, output_zip)
1146
Doug Zongker32b527d2014-03-04 10:03:02 -08001147
Tao Bao9bc6bb22015-11-09 16:58:28 -08001148def WriteVerifyPackage(input_zip, output_zip):
1149 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1150
1151 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1152 recovery_mount_options = OPTIONS.info_dict.get(
1153 "recovery_mount_options")
1154 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001155 if oem_props:
Tao Bao9bc6bb22015-11-09 16:58:28 -08001156 if OPTIONS.oem_source is None:
1157 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -08001158 if not OPTIONS.oem_no_mount:
1159 script.Mount("/oem", recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001160 oem_dict = common.LoadDictionaryFromLines(
1161 open(OPTIONS.oem_source).readlines())
1162
1163 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1164 metadata = {
1165 "post-build": target_fp,
1166 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1167 OPTIONS.info_dict),
1168 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1169 }
1170
1171 device_specific = common.DeviceSpecificParams(
1172 input_zip=input_zip,
1173 input_version=OPTIONS.info_dict["recovery_api_version"],
1174 output_zip=output_zip,
1175 script=script,
1176 input_tmp=OPTIONS.input_tmp,
1177 metadata=metadata,
1178 info_dict=OPTIONS.info_dict)
1179
1180 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1181
1182 script.Print("Verifying device images against %s..." % target_fp)
1183 script.AppendExtra("")
1184
1185 script.Print("Verifying boot...")
1186 boot_img = common.GetBootableImage(
1187 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1188 boot_type, boot_device = common.GetTypeAndDevice(
1189 "/boot", OPTIONS.info_dict)
1190 script.Verify("%s:%s:%d:%s" % (
1191 boot_type, boot_device, boot_img.size, boot_img.sha1))
1192 script.AppendExtra("")
1193
1194 script.Print("Verifying recovery...")
1195 recovery_img = common.GetBootableImage(
1196 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1197 recovery_type, recovery_device = common.GetTypeAndDevice(
1198 "/recovery", OPTIONS.info_dict)
1199 script.Verify("%s:%s:%d:%s" % (
1200 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1201 script.AppendExtra("")
1202
1203 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1204 system_tgt.ResetFileMap()
1205 system_diff = common.BlockDifference("system", system_tgt, src=None)
1206 system_diff.WriteStrictVerifyScript(script)
1207
1208 if HasVendorPartition(input_zip):
1209 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1210 vendor_tgt.ResetFileMap()
1211 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1212 vendor_diff.WriteStrictVerifyScript(script)
1213
1214 # Device specific partitions, such as radio, bootloader and etc.
1215 device_specific.VerifyOTA_Assertions()
1216
1217 script.SetProgress(1.0)
1218 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001219 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001220 WriteMetadata(metadata, output_zip)
1221
1222
Tao Baoc098e9e2016-01-07 13:03:56 -08001223def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1224 source_file=None):
1225 """Generate an Android OTA package that has A/B update payload."""
1226
Alex Deymod8d96ec2016-06-10 16:38:31 -07001227 # The place where the output from the subprocess should go.
1228 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1229
Tao Baoc098e9e2016-01-07 13:03:56 -08001230 # Setup signing keys.
1231 if OPTIONS.package_key is None:
1232 OPTIONS.package_key = OPTIONS.info_dict.get(
1233 "default_system_dev_certificate",
1234 "build/target/product/security/testkey")
1235
Tao Baodea0f8b2016-06-20 17:55:06 -07001236 # A/B updater expects a signing key in RSA format. Gets the key ready for
1237 # later use in step 3, unless a payload_signer has been specified.
1238 if OPTIONS.payload_signer is None:
1239 cmd = ["openssl", "pkcs8",
1240 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1241 "-inform", "DER", "-nocrypt"]
1242 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1243 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001244 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1245 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001246 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001247
Tao Baodea0f8b2016-06-20 17:55:06 -07001248 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001249 temp_zip_file = tempfile.NamedTemporaryFile()
1250 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1251 compression=zipfile.ZIP_DEFLATED)
1252
1253 # Metadata to comply with Android OTA package format.
1254 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1255 oem_dict = None
1256 if oem_props:
1257 if OPTIONS.oem_source is None:
1258 raise common.ExternalError("OEM source required for this build")
1259 oem_dict = common.LoadDictionaryFromLines(
1260 open(OPTIONS.oem_source).readlines())
1261
1262 metadata = {
1263 "post-build": CalculateFingerprint(oem_props, oem_dict,
1264 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001265 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1266 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001267 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1268 OPTIONS.info_dict),
1269 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001270 "ota-required-cache": "0",
1271 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001272 }
1273
1274 if source_file is not None:
1275 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1276 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001277 metadata["pre-build-incremental"] = GetBuildProp(
1278 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001279
1280 # 1. Generate payload.
1281 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1282 cmd = ["brillo_update_payload", "generate",
1283 "--payload", payload_file,
1284 "--target_image", target_file]
1285 if source_file is not None:
1286 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001287 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1288 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001289 assert p1.returncode == 0, "brillo_update_payload generate failed"
1290
1291 # 2. Generate hashes of the payload and metadata files.
1292 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1293 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1294 cmd = ["brillo_update_payload", "hash",
1295 "--unsigned_payload", payload_file,
1296 "--signature_size", "256",
1297 "--metadata_hash_file", metadata_sig_file,
1298 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001299 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1300 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001301 assert p1.returncode == 0, "brillo_update_payload hash failed"
1302
1303 # 3. Sign the hashes and insert them back into the payload file.
1304 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1305 suffix=".bin")
1306 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1307 suffix=".bin")
1308 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001309 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001310 cmd = [OPTIONS.payload_signer]
1311 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001312 else:
1313 cmd = ["openssl", "pkeyutl", "-sign",
1314 "-inkey", rsa_key,
1315 "-pkeyopt", "digest:sha256"]
1316 cmd.extend(["-in", payload_sig_file,
1317 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001318 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1319 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001320 assert p1.returncode == 0, "openssl sign payload failed"
1321
1322 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001323 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001324 cmd = [OPTIONS.payload_signer]
1325 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001326 else:
1327 cmd = ["openssl", "pkeyutl", "-sign",
1328 "-inkey", rsa_key,
1329 "-pkeyopt", "digest:sha256"]
1330 cmd.extend(["-in", metadata_sig_file,
1331 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001332 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1333 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001334 assert p1.returncode == 0, "openssl sign metadata failed"
1335
1336 # 3c. Insert the signatures back into the payload file.
1337 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1338 suffix=".bin")
1339 cmd = ["brillo_update_payload", "sign",
1340 "--unsigned_payload", payload_file,
1341 "--payload", signed_payload_file,
1342 "--signature_size", "256",
1343 "--metadata_signature_file", signed_metadata_sig_file,
1344 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001345 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1346 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001347 assert p1.returncode == 0, "brillo_update_payload sign failed"
1348
Alex Deymo19241c12016-02-04 22:29:29 -08001349 # 4. Dump the signed payload properties.
1350 properties_file = common.MakeTempFile(prefix="payload-properties-",
1351 suffix=".txt")
1352 cmd = ["brillo_update_payload", "properties",
1353 "--payload", signed_payload_file,
1354 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001355 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1356 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001357 assert p1.returncode == 0, "brillo_update_payload properties failed"
1358
Tao Bao7c5dc572016-06-14 17:48:11 -07001359 if OPTIONS.wipe_user_data:
1360 with open(properties_file, "a") as f:
1361 f.write("POWERWASH=1\n")
1362 metadata["ota-wipe"] = "yes"
1363
Alex Deymo19241c12016-02-04 22:29:29 -08001364 # Add the signed payload file and properties into the zip.
1365 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001366 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1367 compress_type=zipfile.ZIP_STORED)
1368 WriteMetadata(metadata, output_zip)
1369
Tianjie Xucfa86222016-03-07 16:31:19 -08001370 # If dm-verity is supported for the device, copy contents of care_map
1371 # into A/B OTA package.
1372 if OPTIONS.info_dict.get("verity") == "true":
1373 target_zip = zipfile.ZipFile(target_file, "r")
1374 care_map_path = "META/care_map.txt"
1375 namelist = target_zip.namelist()
1376 if care_map_path in namelist:
1377 care_map_data = target_zip.read(care_map_path)
1378 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data)
1379 else:
1380 print "Warning: cannot find care map file in target_file package"
1381 common.ZipClose(target_zip)
1382
Tao Baoc098e9e2016-01-07 13:03:56 -08001383 # Sign the whole package to comply with the Android OTA package format.
1384 common.ZipClose(output_zip)
1385 SignOutput(temp_zip_file.name, output_file)
1386 temp_zip_file.close()
1387
1388
Dan Albert8b72aef2015-03-23 19:13:21 -07001389class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001390 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001391 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001392 print "Loading target..."
1393 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1394 print "Loading source..."
1395 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1396
1397 self.verbatim_targets = verbatim_targets = []
1398 self.patch_list = patch_list = []
1399 diffs = []
1400 self.renames = renames = {}
1401 known_paths = set()
1402 largest_source_size = 0
1403
1404 matching_file_cache = {}
1405 for fn, sf in source_data.items():
1406 assert fn == sf.name
1407 matching_file_cache["path:" + fn] = sf
1408 if fn in target_data.keys():
1409 AddToKnownPaths(fn, known_paths)
1410 # Only allow eligibility for filename/sha matching
1411 # if there isn't a perfect path match.
1412 if target_data.get(sf.name) is None:
1413 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1414 matching_file_cache["sha:" + sf.sha1] = sf
1415
1416 for fn in sorted(target_data.keys()):
1417 tf = target_data[fn]
1418 assert fn == tf.name
1419 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1420 if sf is not None and sf.name != tf.name:
1421 print "File has moved from " + sf.name + " to " + tf.name
1422 renames[sf.name] = tf
1423
1424 if sf is None or fn in OPTIONS.require_verbatim:
1425 # This file should be included verbatim
1426 if fn in OPTIONS.prohibit_verbatim:
1427 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1428 print "send", fn, "verbatim"
1429 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001430 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001431 if fn in target_data.keys():
1432 AddToKnownPaths(fn, known_paths)
1433 elif tf.sha1 != sf.sha1:
1434 # File is different; consider sending as a patch
1435 diffs.append(common.Difference(tf, sf))
1436 else:
1437 # Target file data identical to source (may still be renamed)
1438 pass
1439
1440 common.ComputeDifferences(diffs)
1441
1442 for diff in diffs:
1443 tf, sf, d = diff.GetPatch()
1444 path = "/".join(tf.name.split("/")[:-1])
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001445 if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001446 path not in known_paths:
1447 # patch is almost as big as the file; don't bother patching
1448 # or a patch + rename cannot take place due to the target
1449 # directory not existing
1450 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001451 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001452 if sf.name in renames:
1453 del renames[sf.name]
1454 AddToKnownPaths(tf.name, known_paths)
1455 else:
1456 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1457 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1458 largest_source_size = max(largest_source_size, sf.size)
1459
1460 self.largest_source_size = largest_source_size
1461
1462 def EmitVerification(self, script):
1463 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001464 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001465 if tf.name != sf.name:
1466 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1467 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1468 so_far += sf.size
1469 return so_far
1470
Michael Runge63f01de2014-10-28 19:24:19 -07001471 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001472 for fn, _, sha1 in self.verbatim_targets:
1473 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001474 script.FileCheck("/"+fn, sha1)
1475 for tf, _, _, _ in self.patch_list:
1476 script.FileCheck(tf.name, tf.sha1)
1477
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001478 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001479 file_list = ["/" + i[0] for i in self.verbatim_targets]
1480 file_list += ["/" + i for i in self.source_data
1481 if i not in self.target_data and i not in self.renames]
1482 file_list += list(extras)
1483 # Sort the list in descending order, which removes all the files first
1484 # before attempting to remove the folder. (Bug: 22960996)
1485 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001486
1487 def TotalPatchSize(self):
1488 return sum(i[1].size for i in self.patch_list)
1489
1490 def EmitPatches(self, script, total_patch_size, so_far):
1491 self.deferred_patch_list = deferred_patch_list = []
1492 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001493 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001494 if tf.name == "system/build.prop":
1495 deferred_patch_list.append(item)
1496 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001497 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001498 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001499 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1500 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001501 so_far += tf.size
1502 script.SetProgress(so_far / total_patch_size)
1503 return so_far
1504
1505 def EmitDeferredPatches(self, script):
1506 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001507 tf, sf, _, _ = item
1508 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1509 "patch/" + sf.name + ".p")
1510 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001511
1512 def EmitRenames(self, script):
1513 if len(self.renames) > 0:
1514 script.Print("Renaming files...")
1515 for src, tgt in self.renames.iteritems():
1516 print "Renaming " + src + " to " + tgt.name
1517 script.RenameFile(src, tgt.name)
1518
1519
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001520def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001521 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1522 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1523
Doug Zongker26e66192014-02-20 13:22:07 -08001524 if (OPTIONS.block_based and
1525 target_has_recovery_patch and
1526 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001527 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1528
Doug Zongker37974732010-09-16 17:44:38 -07001529 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1530 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001531
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001532 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001533 print("WARNING: generating edify script for a source that "
1534 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001535 script = edify_generator.EdifyGenerator(
1536 source_version, OPTIONS.target_info_dict,
1537 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001538
Tao Bao34b47bf2015-06-22 19:17:41 -07001539 recovery_mount_options = OPTIONS.source_info_dict.get(
1540 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001541 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1542 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -07001543 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001544 if source_oem_props or target_oem_props:
Michael Runge6e836112014-04-15 17:40:21 -07001545 if OPTIONS.oem_source is None:
1546 raise common.ExternalError("OEM source required for this build")
Tao Bao1bb5a182016-03-04 09:45:03 -08001547 if not OPTIONS.oem_no_mount:
1548 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001549 oem_dict = common.LoadDictionaryFromLines(
1550 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001551
Dan Albert8b72aef2015-03-23 19:13:21 -07001552 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001553 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
1554 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001555 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001556 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001557
Tao Bao5d182562016-02-23 11:38:39 -08001558 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
1559 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1560 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1561
1562 if OPTIONS.downgrade:
1563 metadata["ota-downgrade"] = "yes"
1564 if not is_downgrade:
1565 raise RuntimeError("--downgrade specified but no downgrade detected: "
1566 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
1567 else:
1568 if is_downgrade:
1569 # Non-fatal here to allow generating such a package which may require
1570 # manual work to adjust the post-timestamp. A legit use case is that we
1571 # cut a new build C (after having A and B), but want to enfore the
1572 # update path of A -> C -> B. Specifying --downgrade may not help since
1573 # that would enforce a data wipe for C -> B update.
1574 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
1575 "The package may not be deployed properly. "
1576 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
1577 metadata["post-timestamp"] = post_timestamp
1578
Doug Zongker05d3dea2009-06-22 11:32:31 -07001579 device_specific = common.DeviceSpecificParams(
1580 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001581 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001582 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001583 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001584 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001585 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001586 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001587 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001588
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001589 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001590 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001591 if HasVendorPartition(target_zip):
1592 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001593 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001594 else:
1595 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001596
Tao Bao3e30d972016-03-15 13:20:19 -07001597 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001598 OPTIONS.target_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -07001599 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001600 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001601
Tao Bao3e30d972016-03-15 13:20:19 -07001602 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001603 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001604 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001605 script.AssertSomeThumbprint(
1606 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1607 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001608 elif source_oem_props is None and target_oem_props is not None:
1609 script.AssertFingerprintOrThumbprint(
1610 source_fp,
1611 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1612 else:
1613 script.AssertFingerprintOrThumbprint(
1614 target_fp,
1615 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001616
Doug Zongker2ea21062010-04-28 16:05:21 -07001617 metadata["pre-build"] = source_fp
1618 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001619 metadata["pre-build-incremental"] = GetBuildProp(
1620 "ro.build.version.incremental", OPTIONS.source_info_dict)
1621 metadata["post-build-incremental"] = GetBuildProp(
1622 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001623
Doug Zongker55d93282011-01-25 17:03:34 -08001624 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001625 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1626 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001627 target_boot = common.GetBootableImage(
1628 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001629 updating_boot = (not OPTIONS.two_step and
1630 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001631
Doug Zongker55d93282011-01-25 17:03:34 -08001632 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001633 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1634 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001635 target_recovery = common.GetBootableImage(
1636 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001637 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001638
Doug Zongker881dd402009-09-20 14:03:55 -07001639 # Here's how we divide up the progress bar:
1640 # 0.1 for verifying the start state (PatchCheck calls)
1641 # 0.8 for applying patches (ApplyPatch calls)
1642 # 0.1 for unpacking verbatim files, symlinking, and doing the
1643 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001644
Michael Runge6e836112014-04-15 17:40:21 -07001645 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001646 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001647
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001648 # Two-step incremental package strategy (in chronological order,
1649 # which is *not* the order in which the generated script has
1650 # things):
1651 #
1652 # if stage is not "2/3" or "3/3":
1653 # do verification on current system
1654 # write recovery image to boot partition
1655 # set stage to "2/3"
1656 # reboot to boot partition and restart recovery
1657 # else if stage is "2/3":
1658 # write recovery image to recovery partition
1659 # set stage to "3/3"
1660 # reboot to recovery partition and restart recovery
1661 # else:
1662 # (stage must be "3/3")
1663 # perform update:
1664 # patch system files, etc.
1665 # force full install of new boot image
1666 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001667 # complete script normally
1668 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001669
1670 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001671 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001672 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001673 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001674 assert fs.fs_type.upper() == "EMMC", \
1675 "two-step packages only supported on devices with EMMC /misc partitions"
1676 bcb_dev = {"bcb_dev": fs.device}
1677 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1678 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001679if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001680""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001681
1682 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1683 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001684 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001685 script.WriteRawImage("/recovery", "recovery.img")
1686 script.AppendExtra("""
1687set_stage("%(bcb_dev)s", "3/3");
1688reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001689else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001690""" % bcb_dev)
1691
Tao Baod42e97e2016-11-30 12:11:57 -08001692 # Stage 1/3: (a) Verify the current system.
1693 script.Comment("Stage 1/3")
1694
Tao Bao6c55a8a2015-04-08 15:30:27 -07001695 # Dump fingerprints
1696 script.Print("Source: %s" % (source_fp,))
1697 script.Print("Target: %s" % (target_fp,))
1698
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001699 script.Print("Verifying current system...")
1700
Doug Zongkere5ff5902012-01-17 10:55:37 -08001701 device_specific.IncrementalOTA_VerifyBegin()
1702
Doug Zongker881dd402009-09-20 14:03:55 -07001703 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001704 so_far = system_diff.EmitVerification(script)
1705 if vendor_diff:
1706 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001707
Tao Baod8d14be2016-02-04 14:26:02 -08001708 size = []
1709 if system_diff.patch_list:
1710 size.append(system_diff.largest_source_size)
1711 if vendor_diff:
1712 if vendor_diff.patch_list:
1713 size.append(vendor_diff.largest_source_size)
1714
Doug Zongker5da317e2009-06-02 13:38:17 -07001715 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001716 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001717 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001718 print "boot target: %d source: %d diff: %d" % (
1719 target_boot.size, source_boot.size, len(d))
1720
Doug Zongker048e7ca2009-06-15 14:31:53 -07001721 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001722
Tao Baodd24da92015-07-29 14:09:23 -07001723 boot_type, boot_device = common.GetTypeAndDevice(
1724 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001725
1726 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1727 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001728 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001729 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001730 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001731 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001732
Tao Baod8d14be2016-02-04 14:26:02 -08001733 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001734 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001735
Doug Zongker05d3dea2009-06-22 11:32:31 -07001736 device_specific.IncrementalOTA_VerifyEnd()
1737
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001738 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001739 # Stage 1/3: (b) Write recovery image to /boot.
1740 _WriteRecoveryImageToBoot(script, output_zip)
1741
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001742 script.AppendExtra("""
1743set_stage("%(bcb_dev)s", "2/3");
1744reboot_now("%(bcb_dev)s", "");
1745else
1746""" % bcb_dev)
1747
Tao Baod42e97e2016-11-30 12:11:57 -08001748 # Stage 3/3: Make changes.
1749 script.Comment("Stage 3/3")
1750
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001751 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001752
Doug Zongkere5ff5902012-01-17 10:55:37 -08001753 device_specific.IncrementalOTA_InstallBegin()
1754
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001755 if OPTIONS.two_step:
1756 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1757 script.WriteRawImage("/boot", "boot.img")
1758 print "writing full boot image (forced by two-step mode)"
1759
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001760 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001761 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1762 if vendor_diff:
1763 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001764
Doug Zongker881dd402009-09-20 14:03:55 -07001765 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001766 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1767 if vendor_diff:
1768 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001769 if updating_boot:
1770 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001771
1772 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001773 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1774 if vendor_diff:
1775 script.Print("Patching vendor files...")
1776 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001777
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001778 if not OPTIONS.two_step:
1779 if updating_boot:
1780 # Produce the boot image by applying a patch to the current
1781 # contents of the boot partition, and write it back to the
1782 # partition.
1783 script.Print("Patching boot image...")
1784 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1785 % (boot_type, boot_device,
1786 source_boot.size, source_boot.sha1,
1787 target_boot.size, target_boot.sha1),
1788 "-",
1789 target_boot.size, target_boot.sha1,
1790 source_boot.sha1, "patch/boot.img.p")
1791 so_far += target_boot.size
1792 script.SetProgress(so_far / total_patch_size)
1793 print "boot image changed; including."
1794 else:
1795 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001796
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001797 system_items = ItemSet("system", "META/filesystem_config.txt")
1798 if vendor_diff:
1799 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1800
Doug Zongkereef39442009-04-02 12:14:19 -07001801 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001802 # Recovery is generated as a patch using both the boot image
1803 # (which contains the same linux kernel as recovery) and the file
1804 # /system/etc/recovery-resource.dat (which contains all the images
1805 # used in the recovery UI) as sources. This lets us minimize the
1806 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001807 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001808 # For older builds where recovery-resource.dat is not present, we
1809 # use only the boot image as the source.
1810
Doug Zongkerc9253822014-02-04 12:17:58 -08001811 if not target_has_recovery_patch:
1812 def output_sink(fn, data):
1813 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001814 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001815
1816 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1817 target_recovery, target_boot)
1818 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001819 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001820 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001821 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001822 else:
1823 print "recovery image unchanged; skipping."
1824
Doug Zongker881dd402009-09-20 14:03:55 -07001825 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001826
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001827 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1828 if vendor_diff:
1829 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1830
1831 temp_script = script.MakeTemporary()
1832 system_items.GetMetadata(target_zip)
1833 system_items.Get("system").SetPermissions(temp_script)
1834 if vendor_diff:
1835 vendor_items.GetMetadata(target_zip)
1836 vendor_items.Get("vendor").SetPermissions(temp_script)
1837
1838 # Note that this call will mess up the trees of Items, so make sure
1839 # we're done with them.
1840 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1841 if vendor_diff:
1842 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001843
1844 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001845 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1846
1847 # Delete all the symlinks in source that aren't in target. This
1848 # needs to happen before verbatim files are unpacked, in case a
1849 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001850
1851 # If a symlink in the source will be replaced by a regular file, we cannot
1852 # delete the symlink/file in case the package gets applied again. For such
1853 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1854 # (Bug: 23646151)
1855 replaced_symlinks = dict()
1856 if system_diff:
1857 for i in system_diff.verbatim_targets:
1858 replaced_symlinks["/%s" % (i[0],)] = i[2]
1859 if vendor_diff:
1860 for i in vendor_diff.verbatim_targets:
1861 replaced_symlinks["/%s" % (i[0],)] = i[2]
1862
1863 if system_diff:
1864 for tf in system_diff.renames.values():
1865 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1866 if vendor_diff:
1867 for tf in vendor_diff.renames.values():
1868 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1869
1870 always_delete = []
1871 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001872 for dest, link in source_symlinks:
1873 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001874 if link in replaced_symlinks:
1875 may_delete.append((link, replaced_symlinks[link]))
1876 else:
1877 always_delete.append(link)
1878 script.DeleteFiles(always_delete)
1879 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001880
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001881 if system_diff.verbatim_targets:
1882 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001883 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001884 if vendor_diff and vendor_diff.verbatim_targets:
1885 script.Print("Unpacking new vendor files...")
1886 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001887
Doug Zongkerc9253822014-02-04 12:17:58 -08001888 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001889 script.Print("Unpacking new recovery...")
1890 script.UnpackPackageDir("recovery", "/system")
1891
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001892 system_diff.EmitRenames(script)
1893 if vendor_diff:
1894 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001895
Doug Zongker05d3dea2009-06-22 11:32:31 -07001896 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001897
1898 # Create all the symlinks that don't already exist, or point to
1899 # somewhere different than what we want. Delete each symlink before
1900 # creating it, since the 'symlink' command won't overwrite.
1901 to_create = []
1902 for dest, link in target_symlinks:
1903 if link in source_symlinks_d:
1904 if dest != source_symlinks_d[link]:
1905 to_create.append((dest, link))
1906 else:
1907 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001908 script.DeleteFiles([i[1] for i in to_create])
1909 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001910
1911 # Now that the symlinks are created, we can set all the
1912 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001913 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001914
Doug Zongker881dd402009-09-20 14:03:55 -07001915 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001916 device_specific.IncrementalOTA_InstallEnd()
1917
Doug Zongker1c390a22009-05-14 19:06:36 -07001918 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001919 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001920
Doug Zongkere92f15a2011-08-26 13:46:40 -07001921 # Patch the build.prop file last, so if something fails but the
1922 # device can still come up, it appears to be the old build and will
1923 # get set the OTA package again to retry.
1924 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001925 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001926
Doug Zongker922206e2014-03-04 13:16:24 -08001927 if OPTIONS.wipe_user_data:
1928 script.Print("Erasing user data...")
1929 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001930 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001931
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001932 if OPTIONS.two_step:
1933 script.AppendExtra("""
1934set_stage("%(bcb_dev)s", "");
1935endif;
1936endif;
1937""" % bcb_dev)
1938
Michael Runge63f01de2014-10-28 19:24:19 -07001939 if OPTIONS.verify and system_diff:
1940 script.Print("Remounting and verifying system partition files...")
1941 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001942 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001943 system_diff.EmitExplicitTargetVerification(script)
1944
1945 if OPTIONS.verify and vendor_diff:
1946 script.Print("Remounting and verifying vendor partition files...")
1947 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001948 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001949 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08001950
1951 # For downgrade OTAs, we prefer to use the update-binary in the source
1952 # build that is actually newer than the one in the target build.
1953 if OPTIONS.downgrade:
1954 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1955 else:
1956 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001957
Tao Baod8d14be2016-02-04 14:26:02 -08001958 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001959 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001960
1961
1962def main(argv):
1963
1964 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001965 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001966 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001967 elif o in ("-k", "--package_key"):
1968 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001969 elif o in ("-i", "--incremental_from"):
1970 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001971 elif o == "--full_radio":
1972 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001973 elif o == "--full_bootloader":
1974 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001975 elif o in ("-w", "--wipe_user_data"):
1976 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001977 elif o == "--downgrade":
1978 OPTIONS.downgrade = True
1979 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07001980 elif o in ("-o", "--oem_settings"):
1981 OPTIONS.oem_source = a
Tao Bao8608cde2016-02-25 19:49:55 -08001982 elif o == "--oem_no_mount":
1983 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001984 elif o in ("-e", "--extra_script"):
1985 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001986 elif o in ("-t", "--worker_threads"):
1987 if a.isdigit():
1988 OPTIONS.worker_threads = int(a)
1989 else:
1990 raise ValueError("Cannot parse value %r for option %r - only "
1991 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001992 elif o in ("-2", "--two_step"):
1993 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001994 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001995 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001996 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001997 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001998 elif o == "--block":
1999 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002000 elif o in ("-b", "--binary"):
2001 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002002 elif o in ("--no_fallback_to_full",):
2003 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002004 elif o == "--stash_threshold":
2005 try:
2006 OPTIONS.stash_threshold = float(a)
2007 except ValueError:
2008 raise ValueError("Cannot parse value %r for option %r - expecting "
2009 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002010 elif o == "--gen_verify":
2011 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002012 elif o == "--log_diff":
2013 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002014 elif o == "--payload_signer":
2015 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002016 elif o == "--payload_signer_args":
2017 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002018 else:
2019 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002020 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002021
2022 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08002023 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002024 extra_long_opts=[
2025 "board_config=",
2026 "package_key=",
2027 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002028 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002029 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002030 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002031 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07002032 "extra_script=",
2033 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002034 "two_step",
2035 "no_signing",
2036 "block",
2037 "binary=",
2038 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002039 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002040 "verify",
2041 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002042 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002043 "gen_verify",
2044 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002045 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002046 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002047 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002048
2049 if len(args) != 2:
2050 common.Usage(__doc__)
2051 sys.exit(1)
2052
Tao Bao5d182562016-02-23 11:38:39 -08002053 if OPTIONS.downgrade:
2054 # Sanity check to enforce a data wipe.
2055 if not OPTIONS.wipe_user_data:
2056 raise ValueError("Cannot downgrade without a data wipe")
2057
2058 # We should only allow downgrading incrementals (as opposed to full).
2059 # Otherwise the device may go back from arbitrary build with this full
2060 # OTA package.
2061 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002062 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002063
Tao Baoc098e9e2016-01-07 13:03:56 -08002064 # Load the dict file from the zip directly to have a peek at the OTA type.
2065 # For packages using A/B update, unzipping is not needed.
2066 input_zip = zipfile.ZipFile(args[0], "r")
2067 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2068 common.ZipClose(input_zip)
2069
2070 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2071
2072 if ab_update:
2073 if OPTIONS.incremental_source is not None:
2074 OPTIONS.target_info_dict = OPTIONS.info_dict
2075 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2076 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2077 common.ZipClose(source_zip)
2078
2079 if OPTIONS.verbose:
2080 print "--- target info ---"
2081 common.DumpInfoDict(OPTIONS.info_dict)
2082
2083 if OPTIONS.incremental_source is not None:
2084 print "--- source info ---"
2085 common.DumpInfoDict(OPTIONS.source_info_dict)
2086
2087 WriteABOTAPackageWithBrilloScript(
2088 target_file=args[0],
2089 output_file=args[1],
2090 source_file=OPTIONS.incremental_source)
2091
2092 print "done."
2093 return
2094
Doug Zongker1c390a22009-05-14 19:06:36 -07002095 if OPTIONS.extra_script is not None:
2096 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2097
Doug Zongkereef39442009-04-02 12:14:19 -07002098 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08002099 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002100
Doug Zongkereef39442009-04-02 12:14:19 -07002101 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002102 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002103
Doug Zongker37974732010-09-16 17:44:38 -07002104 if OPTIONS.verbose:
2105 print "--- target info ---"
2106 common.DumpInfoDict(OPTIONS.info_dict)
2107
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002108 # If the caller explicitly specified the device-specific extensions
2109 # path via -s/--device_specific, use that. Otherwise, use
2110 # META/releasetools.py if it is present in the target target_files.
2111 # Otherwise, take the path of the file from 'tool_extensions' in the
2112 # info dict and look for that in the local filesystem, relative to
2113 # the current directory.
2114
Doug Zongker37974732010-09-16 17:44:38 -07002115 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002116 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2117 if os.path.exists(from_input):
2118 print "(using device-specific extensions from target_files)"
2119 OPTIONS.device_specific = from_input
2120 else:
2121 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2122
Doug Zongker37974732010-09-16 17:44:38 -07002123 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002124 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002125
Tao Baoc098e9e2016-01-07 13:03:56 -08002126 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002127 raise common.ExternalError(
2128 "--- target build has specified no recovery ---")
2129
Tao Bao767e3ac2015-11-10 12:19:19 -08002130 # Use the default key to sign the package if not specified with package_key.
2131 if not OPTIONS.no_signing:
2132 if OPTIONS.package_key is None:
2133 OPTIONS.package_key = OPTIONS.info_dict.get(
2134 "default_system_dev_certificate",
2135 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002136
Tao Bao767e3ac2015-11-10 12:19:19 -08002137 # Set up the output zip. Create a temporary zip file if signing is needed.
2138 if OPTIONS.no_signing:
2139 if os.path.exists(args[1]):
2140 os.unlink(args[1])
2141 output_zip = zipfile.ZipFile(args[1], "w",
2142 compression=zipfile.ZIP_DEFLATED)
2143 else:
2144 temp_zip_file = tempfile.NamedTemporaryFile()
2145 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2146 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002147
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002148 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002149 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002150 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002151 print "--- can't determine the cache partition size ---"
2152 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002153
Tao Bao9bc6bb22015-11-09 16:58:28 -08002154 # Generate a verify package.
2155 if OPTIONS.gen_verify:
2156 WriteVerifyPackage(input_zip, output_zip)
2157
Tao Bao767e3ac2015-11-10 12:19:19 -08002158 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002159 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002160 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002161
2162 # Generate an incremental OTA. It will fall back to generate a full OTA on
2163 # failure unless no_fallback_to_full is specified.
2164 else:
2165 print "unzipping source target-files..."
2166 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2167 OPTIONS.incremental_source)
2168 OPTIONS.target_info_dict = OPTIONS.info_dict
2169 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2170 OPTIONS.source_tmp)
2171 if OPTIONS.verbose:
2172 print "--- source info ---"
2173 common.DumpInfoDict(OPTIONS.source_info_dict)
2174 try:
2175 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002176 if OPTIONS.log_diff:
2177 out_file = open(OPTIONS.log_diff, 'w')
2178 import target_files_diff
2179 target_files_diff.recursiveDiff('',
2180 OPTIONS.source_tmp,
2181 OPTIONS.input_tmp,
2182 out_file)
2183 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002184 except ValueError:
2185 if not OPTIONS.fallback_to_full:
2186 raise
2187 print "--- failed to build incremental; falling back to full ---"
2188 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002189 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002190
Tao Bao767e3ac2015-11-10 12:19:19 -08002191 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002192
Tao Bao767e3ac2015-11-10 12:19:19 -08002193 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002194 if not OPTIONS.no_signing:
2195 SignOutput(temp_zip_file.name, args[1])
2196 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002197
Doug Zongkereef39442009-04-02 12:14:19 -07002198 print "done."
2199
2200
2201if __name__ == '__main__':
2202 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002203 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002204 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002205 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002206 print
2207 print " ERROR: %s" % (e,)
2208 print
2209 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002210 finally:
2211 common.Cleanup()