blob: 9e9d480f6c99fc7830b2a114ccc9cf13799678ec [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
Doug Zongker962069c2009-04-23 11:41:58 -070067 -n (--no_prereq)
68 Omit the timestamp prereq check normally included at the top of
69 the build scripts (used for developer OTA packages which
70 legitimately need to go back and forth).
71
Tao Bao5d182562016-02-23 11:38:39 -080072 --downgrade
73 Intentionally generate an incremental OTA that updates from a newer
74 build to an older one (based on timestamp comparison). "post-timestamp"
75 will be replaced by "ota-downgrade=yes" in the metadata file. A data
76 wipe will always be enforced, so "ota-wipe=yes" will also be included in
77 the metadata file.
78
Doug Zongker1c390a22009-05-14 19:06:36 -070079 -e (--extra_script) <file>
80 Insert the contents of file at the end of the update script.
81
Hristo Bojinovdafb0422010-08-26 14:35:16 -070082 -a (--aslr_mode) <on|off>
83 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050084
Doug Zongker9b23f2c2013-11-25 14:44:12 -080085 -2 (--two_step)
86 Generate a 'two-step' OTA package, where recovery is updated
87 first, so that any changes made to the system partition are done
88 using the new recovery (new kernel, etc.).
89
Doug Zongker26e66192014-02-20 13:22:07 -080090 --block
91 Generate a block-based OTA if possible. Will fall back to a
92 file-based OTA if the target_files is older and doesn't support
93 block-based OTAs.
94
Doug Zongker25568482014-03-03 10:21:27 -080095 -b (--binary) <file>
96 Use the given binary as the update-binary in the output package,
97 instead of the binary in the build's target_files. Use for
98 development only.
99
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200100 -t (--worker_threads) <int>
101 Specifies the number of worker-threads that will be used when
102 generating patches for incremental updates (defaults to 3).
103
Tao Bao8dcf7382015-05-21 14:09:49 -0700104 --stash_threshold <float>
105 Specifies the threshold that will be used to compute the maximum
106 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800107
108 --gen_verify
109 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800110
111 --log_diff <file>
112 Generate a log file that shows the differences in the source and target
113 builds for an incremental package. This option is only meaningful when
114 -i is specified.
Doug Zongkereef39442009-04-02 12:14:19 -0700115"""
116
117import sys
118
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800119if sys.hexversion < 0x02070000:
120 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -0700121 sys.exit(1)
122
Doug Zongkerfc44a512014-08-26 13:10:25 -0700123import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700124import os
Tao Baoc098e9e2016-01-07 13:03:56 -0800125import subprocess
Doug Zongkereef39442009-04-02 12:14:19 -0700126import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700127import zipfile
128
129import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700130import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700131import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700132
133OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700134OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700135OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700136OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700137OPTIONS.require_verbatim = set()
138OPTIONS.prohibit_verbatim = set(("system/build.prop",))
139OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700140OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700141OPTIONS.omit_prereq = False
Tao Bao5d182562016-02-23 11:38:39 -0800142OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700143OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700144OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700145OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
146if OPTIONS.worker_threads == 0:
147 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800148OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900149OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800150OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800151OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700152OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800153OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700154OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700155OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700156OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700157# Stash size cannot exceed cache_size * threshold.
158OPTIONS.cache_size = None
159OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800160OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800161OPTIONS.log_diff = None
Tao Bao8dcf7382015-05-21 14:09:49 -0700162
Doug Zongkereef39442009-04-02 12:14:19 -0700163def MostPopularKey(d, default):
164 """Given a dict, return the key corresponding to the largest
165 value. Returns 'default' if the dict is empty."""
166 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700167 if not x:
168 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700169 x.sort()
170 return x[-1][1]
171
172
173def IsSymlink(info):
174 """Return true if the zipfile.ZipInfo object passed in represents a
175 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700176 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700177
Hristo Bojinov96be7202010-08-02 10:26:17 -0700178def IsRegular(info):
179 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700180 regular file."""
181 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700182
Michael Runge4038aa82013-12-13 18:06:28 -0800183def ClosestFileMatch(src, tgtfiles, existing):
184 """Returns the closest file match between a source file and list
185 of potential matches. The exact filename match is preferred,
186 then the sha1 is searched for, and finally a file with the same
187 basename is evaluated. Rename support in the updater-binary is
188 required for the latter checks to be used."""
189
190 result = tgtfiles.get("path:" + src.name)
191 if result is not None:
192 return result
193
194 if not OPTIONS.target_info_dict.get("update_rename_support", False):
195 return None
196
197 if src.size < 1000:
198 return None
199
200 result = tgtfiles.get("sha1:" + src.sha1)
201 if result is not None and existing.get(result.name) is None:
202 return result
203 result = tgtfiles.get("file:" + src.name.split("/")[-1])
204 if result is not None and existing.get(result.name) is None:
205 return result
206 return None
207
Dan Albert8b72aef2015-03-23 19:13:21 -0700208class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700209 def __init__(self, partition, fs_config):
210 self.partition = partition
211 self.fs_config = fs_config
212 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700213
Dan Albert8b72aef2015-03-23 19:13:21 -0700214 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700215 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700216 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700217 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700218
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700219 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700220 # The target_files contains a record of what the uid,
221 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700222 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700223
224 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700225 if not line:
226 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700227 columns = line.split()
228 name, uid, gid, mode = columns[:4]
229 selabel = None
230 capabilities = None
231
232 # After the first 4 columns, there are a series of key=value
233 # pairs. Extract out the fields we care about.
234 for element in columns[4:]:
235 key, value = element.split("=")
236 if key == "selabel":
237 selabel = value
238 if key == "capabilities":
239 capabilities = value
240
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700241 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700242 if i is not None:
243 i.uid = int(uid)
244 i.gid = int(gid)
245 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700246 i.selabel = selabel
247 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700248 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700249 i.children.sort(key=lambda i: i.name)
250
Tao Baof2cffbd2015-07-22 12:33:18 -0700251 # Set metadata for the files generated by this script. For full recovery
252 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700253 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700254 if i:
255 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700256 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700257 if i:
258 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700259
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700260
Dan Albert8b72aef2015-03-23 19:13:21 -0700261class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700262 """Items represent the metadata (user, group, mode) of files and
263 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700264 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700265 self.itemset = itemset
266 self.name = name
267 self.uid = None
268 self.gid = None
269 self.mode = None
270 self.selabel = None
271 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700272 self.is_dir = is_dir
273 self.descendants = None
274 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700275
276 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700277 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700278 self.parent.children.append(self)
279 else:
280 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700281 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700282 self.children = []
283
284 def Dump(self, indent=0):
285 if self.uid is not None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700286 print "%s%s %d %d %o" % (
287 " " * indent, self.name, self.uid, self.gid, self.mode)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700288 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700289 print "%s%s %s %s %s" % (
290 " " * indent, self.name, self.uid, self.gid, self.mode)
291 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700292 print "%s%s" % (" "*indent, self.descendants)
293 print "%s%s" % (" "*indent, self.best_subtree)
294 for i in self.children:
295 i.Dump(indent=indent+1)
296
Doug Zongkereef39442009-04-02 12:14:19 -0700297 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700298 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700299 all children and determine the best strategy for using set_perm_recursive
300 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700301 values. Recursively calls itself for all descendants.
302
Dan Albert8b72aef2015-03-23 19:13:21 -0700303 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
304 counting up all descendants of this node. (dmode or fmode may be None.)
305 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
306 fmode, selabel, capabilities) tuple that will match the most descendants of
307 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700308 """
309
Dan Albert8b72aef2015-03-23 19:13:21 -0700310 assert self.is_dir
311 key = (self.uid, self.gid, self.mode, None, self.selabel,
312 self.capabilities)
313 self.descendants = {key: 1}
314 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700315 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700316 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700317 for k, v in i.CountChildMetadata().iteritems():
318 d[k] = d.get(k, 0) + v
319 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700320 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700321 d[k] = d.get(k, 0) + 1
322
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700323 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
324 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700325
326 # First, find the (uid, gid) pair that matches the most
327 # descendants.
328 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700329 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700330 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
331 ug = MostPopularKey(ug, (0, 0))
332
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700333 # Now find the dmode, fmode, selabel, and capabilities that match
334 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700335 best_dmode = (0, 0o755)
336 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700337 best_selabel = (0, None)
338 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700339 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700340 if k[:2] != ug:
341 continue
342 if k[2] is not None and count >= best_dmode[0]:
343 best_dmode = (count, k[2])
344 if k[3] is not None and count >= best_fmode[0]:
345 best_fmode = (count, k[3])
346 if k[4] is not None and count >= best_selabel[0]:
347 best_selabel = (count, k[4])
348 if k[5] is not None and count >= best_capabilities[0]:
349 best_capabilities = (count, k[5])
350 self.best_subtree = ug + (
351 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700352
353 return d
354
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700355 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700356 """Append set_perm/set_perm_recursive commands to 'script' to
357 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700358 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700359
360 self.CountChildMetadata()
361
362 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700363 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
364 # that the current item (and all its children) have already been set to.
365 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700366 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700367 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700368 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700369 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700370 current = item.best_subtree
371
372 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700373 item.mode != current[2] or item.selabel != current[4] or \
374 item.capabilities != current[5]:
375 script.SetPermissions("/"+item.name, item.uid, item.gid,
376 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700377
378 for i in item.children:
379 recurse(i, current)
380 else:
381 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700382 item.mode != current[3] or item.selabel != current[4] or \
383 item.capabilities != current[5]:
384 script.SetPermissions("/"+item.name, item.uid, item.gid,
385 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700386
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700387 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700388
389
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700390def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
391 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700392 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800393 list of symlinks. output_zip may be None, in which case the copy is
394 skipped (but the other side effects still happen). substitute is an
395 optional dict of {output filename: contents} to be output instead of
396 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700397 """
398
399 symlinks = []
400
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700401 partition = itemset.partition
402
Doug Zongkereef39442009-04-02 12:14:19 -0700403 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700404 prefix = partition.upper() + "/"
405 if info.filename.startswith(prefix):
406 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700407 if IsSymlink(info):
408 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700409 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700410 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700411 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700412 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700413 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700414 if substitute and fn in substitute and substitute[fn] is None:
415 continue
416 if output_zip is not None:
417 if substitute and fn in substitute:
418 data = substitute[fn]
419 else:
420 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700421 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700422 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700423 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700424 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700425 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700426
427 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800428 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700429
430
Doug Zongkereef39442009-04-02 12:14:19 -0700431def SignOutput(temp_zip_name, output_zip_name):
432 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
433 pw = key_passwords[OPTIONS.package_key]
434
Doug Zongker951495f2009-08-14 12:44:19 -0700435 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
436 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700437
438
Dan Albert8b72aef2015-03-23 19:13:21 -0700439def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700440 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Baoc0863702016-03-07 13:03:19 -0800441 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700442 device = GetBuildProp("ro.product.device", info_dict)
443 script.AssertDevice(device)
444 else:
445 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700446 raise common.ExternalError(
447 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700448 for prop in oem_props.split():
449 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700450 raise common.ExternalError(
451 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700452 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700453
Doug Zongkereef39442009-04-02 12:14:19 -0700454
Doug Zongkerc9253822014-02-04 12:17:58 -0800455def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700456 namelist = [name for name in target_files_zip.namelist()]
457 return ("SYSTEM/recovery-from-boot.p" in namelist or
458 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700459
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700460def HasVendorPartition(target_files_zip):
461 try:
462 target_files_zip.getinfo("VENDOR/")
463 return True
464 except KeyError:
465 return False
466
Michael Runge6e836112014-04-15 17:40:21 -0700467def GetOemProperty(name, oem_props, oem_dict, info_dict):
468 if oem_props is not None and name in oem_props:
469 return oem_dict[name]
470 return GetBuildProp(name, info_dict)
471
472
473def CalculateFingerprint(oem_props, oem_dict, info_dict):
474 if oem_props is None:
475 return GetBuildProp("ro.build.fingerprint", info_dict)
476 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700477 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
478 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
479 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
480 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700481
Doug Zongkerfc44a512014-08-26 13:10:25 -0700482
Doug Zongker3c84f562014-07-31 11:06:30 -0700483def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700484 # Return an image object (suitable for passing to BlockImageDiff)
485 # for the 'which' partition (most be "system" or "vendor"). If a
486 # prebuilt image and file map are found in tmpdir they are used,
487 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700488
489 assert which in ("system", "vendor")
490
491 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700492 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
493 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700494 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700495 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700496
497 else:
498 print "building %s.img from target-files" % (which,)
499
500 # This is an 'old' target-files, which does not contain images
501 # already built. Build them.
502
Doug Zongkerfc44a512014-08-26 13:10:25 -0700503 mappath = tempfile.mkstemp()[1]
504 OPTIONS.tempfiles.append(mappath)
505
Doug Zongker3c84f562014-07-31 11:06:30 -0700506 import add_img_to_target_files
507 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700508 path = add_img_to_target_files.BuildSystem(
509 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700510 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700511 path = add_img_to_target_files.BuildVendor(
512 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700513
Tao Baoff777812015-05-12 11:42:31 -0700514 # Bug: http://b/20939131
515 # In ext4 filesystems, block 0 might be changed even being mounted
516 # R/O. We add it to clobbered_blocks so that it will be written to the
517 # target unconditionally. Note that they are still part of care_map.
518 clobbered_blocks = "0"
519
520 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700521
522
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700523def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700524 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700525 # be installed on top of. For now, we expect the API just won't
526 # change very often. Similarly for fstab, it might have changed
527 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700528 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700529
Michael Runge7cd99ba2014-10-22 17:21:48 -0700530 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Baoc0863702016-03-07 13:03:19 -0800531 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -0700532 oem_dict = None
Tao Baoc0863702016-03-07 13:03:19 -0800533 if oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700534 if OPTIONS.oem_source is None:
535 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800536 if not OPTIONS.oem_no_mount:
537 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700538 oem_dict = common.LoadDictionaryFromLines(
539 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700540
Tao Baoc0863702016-03-07 13:03:19 -0800541 target_fp = CalculateFingerprint(oem_props, oem_dict,
542 OPTIONS.target_info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700543 metadata = {
Tao Baoc0863702016-03-07 13:03:19 -0800544 "post-build": target_fp,
Dan Albert8b72aef2015-03-23 19:13:21 -0700545 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
546 OPTIONS.info_dict),
547 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
548 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700549
Doug Zongker05d3dea2009-06-22 11:32:31 -0700550 device_specific = common.DeviceSpecificParams(
551 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700552 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700553 output_zip=output_zip,
554 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700555 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700556 metadata=metadata,
557 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700558
Doug Zongkerc9253822014-02-04 12:17:58 -0800559 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800560 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800561
Tao Baod8d14be2016-02-04 14:26:02 -0800562 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
563
Doug Zongker962069c2009-04-23 11:41:58 -0700564 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700565 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700566 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
567 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700568
Michael Runge6e836112014-04-15 17:40:21 -0700569 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700570 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800571
572 # Two-step package strategy (in chronological order, which is *not*
573 # the order in which the generated script has things):
574 #
575 # if stage is not "2/3" or "3/3":
576 # write recovery image to boot partition
577 # set stage to "2/3"
578 # reboot to boot partition and restart recovery
579 # else if stage is "2/3":
580 # write recovery image to recovery partition
581 # set stage to "3/3"
582 # reboot to recovery partition and restart recovery
583 # else:
584 # (stage must be "3/3")
585 # set stage to ""
586 # do normal full package installation:
587 # wipe and install system, boot image, etc.
588 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700589 # complete script normally
590 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800591
592 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
593 OPTIONS.input_tmp, "RECOVERY")
594 if OPTIONS.two_step:
595 if not OPTIONS.info_dict.get("multistage_support", None):
596 assert False, "two-step packages not supported by this build"
597 fs = OPTIONS.info_dict["fstab"]["/misc"]
598 assert fs.fs_type.upper() == "EMMC", \
599 "two-step packages only supported on devices with EMMC /misc partitions"
600 bcb_dev = {"bcb_dev": fs.device}
601 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
602 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700603if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800604""" % bcb_dev)
605 script.WriteRawImage("/recovery", "recovery.img")
606 script.AppendExtra("""
607set_stage("%(bcb_dev)s", "3/3");
608reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700609else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800610""" % bcb_dev)
611
Tao Bao6c55a8a2015-04-08 15:30:27 -0700612 # Dump fingerprints
Tao Baoc0863702016-03-07 13:03:19 -0800613 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700614
Doug Zongkere5ff5902012-01-17 10:55:37 -0800615 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700616
Doug Zongker01ce19c2014-02-04 13:48:15 -0800617 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700618
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700619 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800620 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700621 if HasVendorPartition(input_zip):
622 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700623
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400624 # Place a copy of file_contexts.bin into the OTA package which will be used
625 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700626 if "selinux_fc" in OPTIONS.info_dict:
627 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500628
Michael Runge7cd99ba2014-10-22 17:21:48 -0700629 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
630
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700631 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700632 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800633
Doug Zongker26e66192014-02-20 13:22:07 -0800634 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700635 # Full OTA is done as an "incremental" against an empty source
636 # image. This has the effect of writing new data from the package
637 # to the entire partition, but lets us reuse the updater code that
638 # writes incrementals to do it.
639 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
640 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700641 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700642 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800643 else:
644 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700645 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800646 if not has_recovery_patch:
647 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800648 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700649
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700650 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800651 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700652
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700653 boot_img = common.GetBootableImage(
654 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800655
Doug Zongker91a99c22014-05-09 13:15:01 -0700656 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800657 def output_sink(fn, data):
658 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700659 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800660
661 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
662 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700663
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700664 system_items.GetMetadata(input_zip)
665 system_items.Get("system").SetPermissions(script)
666
667 if HasVendorPartition(input_zip):
668 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
669 script.ShowProgress(0.1, 0)
670
671 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700672 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
673 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700674 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700675 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700676 else:
677 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700678 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700679 script.UnpackPackageDir("vendor", "/vendor")
680
681 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
682 script.MakeSymlinks(symlinks)
683
684 vendor_items.GetMetadata(input_zip)
685 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700686
Doug Zongker37974732010-09-16 17:44:38 -0700687 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700688 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700689
Doug Zongker01ce19c2014-02-04 13:48:15 -0800690 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700691 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700692
Doug Zongker01ce19c2014-02-04 13:48:15 -0800693 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700694 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700695
Doug Zongker1c390a22009-05-14 19:06:36 -0700696 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700697 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700698
Doug Zongker14833602010-02-02 13:12:04 -0800699 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800700
Doug Zongker922206e2014-03-04 13:16:24 -0800701 if OPTIONS.wipe_user_data:
702 script.ShowProgress(0.1, 10)
703 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700704
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800705 if OPTIONS.two_step:
706 script.AppendExtra("""
707set_stage("%(bcb_dev)s", "");
708""" % bcb_dev)
709 script.AppendExtra("else\n")
710 script.WriteRawImage("/boot", "recovery.img")
711 script.AppendExtra("""
712set_stage("%(bcb_dev)s", "2/3");
713reboot_now("%(bcb_dev)s", "");
714endif;
715endif;
716""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800717
Tao Bao5d182562016-02-23 11:38:39 -0800718 script.SetProgress(1)
719 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800720 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700721 WriteMetadata(metadata, output_zip)
722
Doug Zongkerfc44a512014-08-26 13:10:25 -0700723
Dan Albert8e0178d2015-01-27 15:53:15 -0800724def WritePolicyConfig(file_name, output_zip):
725 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500726
Doug Zongker2ea21062010-04-28 16:05:21 -0700727
728def WriteMetadata(metadata, output_zip):
729 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
730 "".join(["%s=%s\n" % kv
731 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700732
Doug Zongkerfc44a512014-08-26 13:10:25 -0700733
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700734def LoadPartitionFiles(z, partition):
735 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700736 ZipFile, and return a dict of {filename: File object}."""
737 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700738 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700739 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700740 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700741 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700742 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700743 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700744 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800745 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700746
747
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700748def GetBuildProp(prop, info_dict):
749 """Return the fingerprint of the build of a given target-files info_dict."""
750 try:
751 return info_dict.get("build.prop", {})[prop]
752 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700753 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700754
Doug Zongkerfc44a512014-08-26 13:10:25 -0700755
Michael Runge4038aa82013-12-13 18:06:28 -0800756def AddToKnownPaths(filename, known_paths):
757 if filename[-1] == "/":
758 return
759 dirs = filename.split("/")[:-1]
760 while len(dirs) > 0:
761 path = "/".join(dirs)
762 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700763 break
Michael Runge4038aa82013-12-13 18:06:28 -0800764 known_paths.add(path)
765 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700766
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700767
Geremy Condra36bd3652014-02-06 19:45:10 -0800768def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700769 # TODO(tbao): We should factor out the common parts between
770 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800771 source_version = OPTIONS.source_info_dict["recovery_api_version"]
772 target_version = OPTIONS.target_info_dict["recovery_api_version"]
773
774 if source_version == 0:
Tao Baoc0863702016-03-07 13:03:19 -0800775 print("WARNING: generating edify script for a source that "
776 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700777 script = edify_generator.EdifyGenerator(
778 source_version, OPTIONS.target_info_dict,
779 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800780
Tao Bao3806c232015-07-05 21:08:33 -0700781 recovery_mount_options = OPTIONS.source_info_dict.get(
782 "recovery_mount_options")
Tao Baoc0863702016-03-07 13:03:19 -0800783 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
784 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Tao Bao3806c232015-07-05 21:08:33 -0700785 oem_dict = None
Tao Baoc0863702016-03-07 13:03:19 -0800786 if source_oem_props or target_oem_props:
Tao Bao3806c232015-07-05 21:08:33 -0700787 if OPTIONS.oem_source is None:
788 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800789 if not OPTIONS.oem_no_mount:
790 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700791 oem_dict = common.LoadDictionaryFromLines(
792 open(OPTIONS.oem_source).readlines())
793
Dan Albert8b72aef2015-03-23 19:13:21 -0700794 metadata = {
Tao Baoc0863702016-03-07 13:03:19 -0800795 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
796 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800797 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700798 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800799
Tao Bao5d182562016-02-23 11:38:39 -0800800 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
801 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
802 is_downgrade = long(post_timestamp) < long(pre_timestamp)
803
804 if OPTIONS.downgrade:
805 metadata["ota-downgrade"] = "yes"
806 if not is_downgrade:
807 raise RuntimeError("--downgrade specified but no downgrade detected: "
808 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
809 else:
810 if is_downgrade:
811 # Non-fatal here to allow generating such a package which may require
812 # manual work to adjust the post-timestamp. A legit use case is that we
813 # cut a new build C (after having A and B), but want to enfore the
814 # update path of A -> C -> B. Specifying --downgrade may not help since
815 # that would enforce a data wipe for C -> B update.
816 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
817 "The package may not be deployed properly. "
818 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
819 metadata["post-timestamp"] = post_timestamp
820
Geremy Condra36bd3652014-02-06 19:45:10 -0800821 device_specific = common.DeviceSpecificParams(
822 source_zip=source_zip,
823 source_version=source_version,
824 target_zip=target_zip,
825 target_version=target_version,
826 output_zip=output_zip,
827 script=script,
828 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700829 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800830
Tao Baoc0863702016-03-07 13:03:19 -0800831 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700832 OPTIONS.source_info_dict)
Tao Baoc0863702016-03-07 13:03:19 -0800833 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700834 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800835 metadata["pre-build"] = source_fp
836 metadata["post-build"] = target_fp
837
838 source_boot = common.GetBootableImage(
839 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
840 OPTIONS.source_info_dict)
841 target_boot = common.GetBootableImage(
842 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
843 updating_boot = (not OPTIONS.two_step and
844 (source_boot.data != target_boot.data))
845
Geremy Condra36bd3652014-02-06 19:45:10 -0800846 target_recovery = common.GetBootableImage(
847 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800848
Doug Zongkerfc44a512014-08-26 13:10:25 -0700849 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
850 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700851
852 blockimgdiff_version = 1
853 if OPTIONS.info_dict:
854 blockimgdiff_version = max(
855 int(i) for i in
856 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
857
Tianjie Xufc3422a2015-12-15 11:53:59 -0800858 # Check first block of system partition for remount R/W only if
859 # disk type is ext4
860 system_partition = OPTIONS.source_info_dict["fstab"]["/system"]
Tao Baod8d14be2016-02-04 14:26:02 -0800861 check_first_block = system_partition.fs_type == "ext4"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700862 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800863 check_first_block,
Tao Baodd2a5892015-03-12 12:32:37 -0700864 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700865
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700866 if HasVendorPartition(target_zip):
867 if not HasVendorPartition(source_zip):
868 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700869 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
870 OPTIONS.source_info_dict)
871 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
872 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800873
874 # Check first block of vendor partition for remount R/W only if
875 # disk type is ext4
876 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800877 check_first_block = vendor_partition.fs_type == "ext4"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700878 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800879 check_first_block,
Tao Baodd2a5892015-03-12 12:32:37 -0700880 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700881 else:
882 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800883
Michael Rungec6e3afd2014-05-05 11:55:47 -0700884 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800885 device_specific.IncrementalOTA_Assertions()
886
887 # Two-step incremental package strategy (in chronological order,
888 # which is *not* the order in which the generated script has
889 # things):
890 #
891 # if stage is not "2/3" or "3/3":
892 # do verification on current system
893 # write recovery image to boot partition
894 # set stage to "2/3"
895 # reboot to boot partition and restart recovery
896 # else if stage is "2/3":
897 # write recovery image to recovery partition
898 # set stage to "3/3"
899 # reboot to recovery partition and restart recovery
900 # else:
901 # (stage must be "3/3")
902 # perform update:
903 # patch system files, etc.
904 # force full install of new boot image
905 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700906 # complete script normally
907 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800908
909 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700910 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800911 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700912 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800913 assert fs.fs_type.upper() == "EMMC", \
914 "two-step packages only supported on devices with EMMC /misc partitions"
915 bcb_dev = {"bcb_dev": fs.device}
916 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
917 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700918if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800919""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -0700920 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800921 script.WriteRawImage("/recovery", "recovery.img")
922 script.AppendExtra("""
923set_stage("%(bcb_dev)s", "3/3");
924reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700925else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800926""" % bcb_dev)
927
Tao Bao6c55a8a2015-04-08 15:30:27 -0700928 # Dump fingerprints
Tao Baoc0863702016-03-07 13:03:19 -0800929 script.Print(source_fp)
930 script.Print(target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700931
Geremy Condra36bd3652014-02-06 19:45:10 -0800932 script.Print("Verifying current system...")
933
934 device_specific.IncrementalOTA_VerifyBegin()
935
Tao Baoc0863702016-03-07 13:03:19 -0800936 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
937 # patching on a device that's already on the target build will damage the
938 # system. Because operations like move don't check the block state, they
939 # always apply the changes unconditionally.
940 if blockimgdiff_version <= 2:
941 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700942 script.AssertSomeFingerprint(source_fp)
943 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700944 script.AssertSomeThumbprint(
945 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Baoc0863702016-03-07 13:03:19 -0800946
947 else: # blockimgdiff_version > 2
948 if source_oem_props is None and target_oem_props is None:
949 script.AssertSomeFingerprint(source_fp, target_fp)
950 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -0700951 script.AssertSomeThumbprint(
952 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
953 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Baoc0863702016-03-07 13:03:19 -0800954 elif source_oem_props is None and target_oem_props is not None:
955 script.AssertFingerprintOrThumbprint(
956 source_fp,
957 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
958 else:
959 script.AssertFingerprintOrThumbprint(
960 target_fp,
961 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800962
Tao Baod8d14be2016-02-04 14:26:02 -0800963 # Check the required cache size (i.e. stashed blocks).
964 size = []
965 if system_diff:
966 size.append(system_diff.required_cache)
967 if vendor_diff:
968 size.append(vendor_diff.required_cache)
969
Geremy Condra36bd3652014-02-06 19:45:10 -0800970 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700971 boot_type, boot_device = common.GetTypeAndDevice(
972 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800973 d = common.Difference(target_boot, source_boot)
974 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700975 if d is None:
976 include_full_boot = True
977 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
978 else:
979 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800980
Doug Zongkerf8340082014-08-05 10:39:37 -0700981 print "boot target: %d source: %d diff: %d" % (
982 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -0800983
Doug Zongkerf8340082014-08-05 10:39:37 -0700984 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800985
Doug Zongkerf8340082014-08-05 10:39:37 -0700986 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
987 (boot_type, boot_device,
988 source_boot.size, source_boot.sha1,
989 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800990 size.append(target_boot.size)
991
992 if size:
993 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800994
995 device_specific.IncrementalOTA_VerifyEnd()
996
997 if OPTIONS.two_step:
998 script.WriteRawImage("/boot", "recovery.img")
999 script.AppendExtra("""
1000set_stage("%(bcb_dev)s", "2/3");
1001reboot_now("%(bcb_dev)s", "");
1002else
1003""" % bcb_dev)
1004
Jesse Zhao75bcea02015-01-06 10:59:53 -08001005 # Verify the existing partitions.
1006 system_diff.WriteVerifyScript(script)
1007 if vendor_diff:
1008 vendor_diff.WriteVerifyScript(script)
1009
Geremy Condra36bd3652014-02-06 19:45:10 -08001010 script.Comment("---- start making changes here ----")
1011
1012 device_specific.IncrementalOTA_InstallBegin()
1013
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001014 system_diff.WriteScript(script, output_zip,
1015 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001016
Doug Zongkerfc44a512014-08-26 13:10:25 -07001017 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001018 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001019
1020 if OPTIONS.two_step:
1021 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1022 script.WriteRawImage("/boot", "boot.img")
1023 print "writing full boot image (forced by two-step mode)"
1024
1025 if not OPTIONS.two_step:
1026 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001027 if include_full_boot:
1028 print "boot image changed; including full."
1029 script.Print("Installing boot image...")
1030 script.WriteRawImage("/boot", "boot.img")
1031 else:
1032 # Produce the boot image by applying a patch to the current
1033 # contents of the boot partition, and write it back to the
1034 # partition.
1035 print "boot image changed; including patch."
1036 script.Print("Patching boot image...")
1037 script.ShowProgress(0.1, 10)
1038 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1039 % (boot_type, boot_device,
1040 source_boot.size, source_boot.sha1,
1041 target_boot.size, target_boot.sha1),
1042 "-",
1043 target_boot.size, target_boot.sha1,
1044 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001045 else:
1046 print "boot image unchanged; skipping."
1047
1048 # Do device-specific installation (eg, write radio image).
1049 device_specific.IncrementalOTA_InstallEnd()
1050
1051 if OPTIONS.extra_script is not None:
1052 script.AppendExtra(OPTIONS.extra_script)
1053
Doug Zongker922206e2014-03-04 13:16:24 -08001054 if OPTIONS.wipe_user_data:
1055 script.Print("Erasing user data...")
1056 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001057 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001058
Geremy Condra36bd3652014-02-06 19:45:10 -08001059 if OPTIONS.two_step:
1060 script.AppendExtra("""
1061set_stage("%(bcb_dev)s", "");
1062endif;
1063endif;
1064""" % bcb_dev)
1065
1066 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -08001067 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001068 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001069 WriteMetadata(metadata, output_zip)
1070
Doug Zongker32b527d2014-03-04 10:03:02 -08001071
Tao Bao9bc6bb22015-11-09 16:58:28 -08001072def WriteVerifyPackage(input_zip, output_zip):
1073 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1074
1075 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1076 recovery_mount_options = OPTIONS.info_dict.get(
1077 "recovery_mount_options")
1078 oem_dict = None
Tao Baoc0863702016-03-07 13:03:19 -08001079 if oem_props:
Tao Bao9bc6bb22015-11-09 16:58:28 -08001080 if OPTIONS.oem_source is None:
1081 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -08001082 if not OPTIONS.oem_no_mount:
1083 script.Mount("/oem", recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001084 oem_dict = common.LoadDictionaryFromLines(
1085 open(OPTIONS.oem_source).readlines())
1086
1087 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1088 metadata = {
1089 "post-build": target_fp,
1090 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1091 OPTIONS.info_dict),
1092 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1093 }
1094
1095 device_specific = common.DeviceSpecificParams(
1096 input_zip=input_zip,
1097 input_version=OPTIONS.info_dict["recovery_api_version"],
1098 output_zip=output_zip,
1099 script=script,
1100 input_tmp=OPTIONS.input_tmp,
1101 metadata=metadata,
1102 info_dict=OPTIONS.info_dict)
1103
1104 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1105
1106 script.Print("Verifying device images against %s..." % target_fp)
1107 script.AppendExtra("")
1108
1109 script.Print("Verifying boot...")
1110 boot_img = common.GetBootableImage(
1111 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1112 boot_type, boot_device = common.GetTypeAndDevice(
1113 "/boot", OPTIONS.info_dict)
1114 script.Verify("%s:%s:%d:%s" % (
1115 boot_type, boot_device, boot_img.size, boot_img.sha1))
1116 script.AppendExtra("")
1117
1118 script.Print("Verifying recovery...")
1119 recovery_img = common.GetBootableImage(
1120 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1121 recovery_type, recovery_device = common.GetTypeAndDevice(
1122 "/recovery", OPTIONS.info_dict)
1123 script.Verify("%s:%s:%d:%s" % (
1124 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1125 script.AppendExtra("")
1126
1127 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1128 system_tgt.ResetFileMap()
1129 system_diff = common.BlockDifference("system", system_tgt, src=None)
1130 system_diff.WriteStrictVerifyScript(script)
1131
1132 if HasVendorPartition(input_zip):
1133 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1134 vendor_tgt.ResetFileMap()
1135 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1136 vendor_diff.WriteStrictVerifyScript(script)
1137
1138 # Device specific partitions, such as radio, bootloader and etc.
1139 device_specific.VerifyOTA_Assertions()
1140
1141 script.SetProgress(1.0)
1142 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001143 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001144 WriteMetadata(metadata, output_zip)
1145
1146
Tao Baoc098e9e2016-01-07 13:03:56 -08001147def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1148 source_file=None):
1149 """Generate an Android OTA package that has A/B update payload."""
1150
1151 # Setup signing keys.
1152 if OPTIONS.package_key is None:
1153 OPTIONS.package_key = OPTIONS.info_dict.get(
1154 "default_system_dev_certificate",
1155 "build/target/product/security/testkey")
1156
1157 # A/B updater expects key in RSA format.
1158 cmd = ["openssl", "pkcs8",
1159 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1160 "-inform", "DER", "-nocrypt"]
1161 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1162 cmd.extend(["-out", rsa_key])
1163 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1164 p1.wait()
1165 assert p1.returncode == 0, "openssl pkcs8 failed"
1166
1167 # Stage the output zip package for signing.
1168 temp_zip_file = tempfile.NamedTemporaryFile()
1169 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1170 compression=zipfile.ZIP_DEFLATED)
1171
1172 # Metadata to comply with Android OTA package format.
1173 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1174 oem_dict = None
1175 if oem_props:
1176 if OPTIONS.oem_source is None:
1177 raise common.ExternalError("OEM source required for this build")
1178 oem_dict = common.LoadDictionaryFromLines(
1179 open(OPTIONS.oem_source).readlines())
1180
1181 metadata = {
1182 "post-build": CalculateFingerprint(oem_props, oem_dict,
1183 OPTIONS.info_dict),
1184 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1185 OPTIONS.info_dict),
1186 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001187 "ota-required-cache": "0",
1188 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001189 }
1190
1191 if source_file is not None:
1192 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1193 OPTIONS.source_info_dict)
1194
1195 # 1. Generate payload.
1196 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1197 cmd = ["brillo_update_payload", "generate",
1198 "--payload", payload_file,
1199 "--target_image", target_file]
1200 if source_file is not None:
1201 cmd.extend(["--source_image", source_file])
1202 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1203 p1.wait()
1204 assert p1.returncode == 0, "brillo_update_payload generate failed"
1205
1206 # 2. Generate hashes of the payload and metadata files.
1207 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1208 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1209 cmd = ["brillo_update_payload", "hash",
1210 "--unsigned_payload", payload_file,
1211 "--signature_size", "256",
1212 "--metadata_hash_file", metadata_sig_file,
1213 "--payload_hash_file", payload_sig_file]
1214 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1215 p1.wait()
1216 assert p1.returncode == 0, "brillo_update_payload hash failed"
1217
1218 # 3. Sign the hashes and insert them back into the payload file.
1219 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1220 suffix=".bin")
1221 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1222 suffix=".bin")
1223 # 3a. Sign the payload hash.
1224 cmd = ["openssl", "pkeyutl", "-sign",
1225 "-inkey", rsa_key,
1226 "-pkeyopt", "digest:sha256",
1227 "-in", payload_sig_file,
1228 "-out", signed_payload_sig_file]
1229 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1230 p1.wait()
1231 assert p1.returncode == 0, "openssl sign payload failed"
1232
1233 # 3b. Sign the metadata hash.
1234 cmd = ["openssl", "pkeyutl", "-sign",
1235 "-inkey", rsa_key,
1236 "-pkeyopt", "digest:sha256",
1237 "-in", metadata_sig_file,
1238 "-out", signed_metadata_sig_file]
1239 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1240 p1.wait()
1241 assert p1.returncode == 0, "openssl sign metadata failed"
1242
1243 # 3c. Insert the signatures back into the payload file.
1244 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1245 suffix=".bin")
1246 cmd = ["brillo_update_payload", "sign",
1247 "--unsigned_payload", payload_file,
1248 "--payload", signed_payload_file,
1249 "--signature_size", "256",
1250 "--metadata_signature_file", signed_metadata_sig_file,
1251 "--payload_signature_file", signed_payload_sig_file]
1252 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1253 p1.wait()
1254 assert p1.returncode == 0, "brillo_update_payload sign failed"
1255
Alex Deymo19241c12016-02-04 22:29:29 -08001256 # 4. Dump the signed payload properties.
1257 properties_file = common.MakeTempFile(prefix="payload-properties-",
1258 suffix=".txt")
1259 cmd = ["brillo_update_payload", "properties",
1260 "--payload", signed_payload_file,
1261 "--properties_file", properties_file]
1262 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1263 p1.wait()
1264 assert p1.returncode == 0, "brillo_update_payload properties failed"
1265
1266 # Add the signed payload file and properties into the zip.
1267 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001268 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1269 compress_type=zipfile.ZIP_STORED)
1270 WriteMetadata(metadata, output_zip)
1271
1272 # Sign the whole package to comply with the Android OTA package format.
1273 common.ZipClose(output_zip)
1274 SignOutput(temp_zip_file.name, output_file)
1275 temp_zip_file.close()
1276
1277
Dan Albert8b72aef2015-03-23 19:13:21 -07001278class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001279 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001280 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001281 print "Loading target..."
1282 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1283 print "Loading source..."
1284 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1285
1286 self.verbatim_targets = verbatim_targets = []
1287 self.patch_list = patch_list = []
1288 diffs = []
1289 self.renames = renames = {}
1290 known_paths = set()
1291 largest_source_size = 0
1292
1293 matching_file_cache = {}
1294 for fn, sf in source_data.items():
1295 assert fn == sf.name
1296 matching_file_cache["path:" + fn] = sf
1297 if fn in target_data.keys():
1298 AddToKnownPaths(fn, known_paths)
1299 # Only allow eligibility for filename/sha matching
1300 # if there isn't a perfect path match.
1301 if target_data.get(sf.name) is None:
1302 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1303 matching_file_cache["sha:" + sf.sha1] = sf
1304
1305 for fn in sorted(target_data.keys()):
1306 tf = target_data[fn]
1307 assert fn == tf.name
1308 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1309 if sf is not None and sf.name != tf.name:
1310 print "File has moved from " + sf.name + " to " + tf.name
1311 renames[sf.name] = tf
1312
1313 if sf is None or fn in OPTIONS.require_verbatim:
1314 # This file should be included verbatim
1315 if fn in OPTIONS.prohibit_verbatim:
1316 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1317 print "send", fn, "verbatim"
1318 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001319 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001320 if fn in target_data.keys():
1321 AddToKnownPaths(fn, known_paths)
1322 elif tf.sha1 != sf.sha1:
1323 # File is different; consider sending as a patch
1324 diffs.append(common.Difference(tf, sf))
1325 else:
1326 # Target file data identical to source (may still be renamed)
1327 pass
1328
1329 common.ComputeDifferences(diffs)
1330
1331 for diff in diffs:
1332 tf, sf, d = diff.GetPatch()
1333 path = "/".join(tf.name.split("/")[:-1])
1334 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1335 path not in known_paths:
1336 # patch is almost as big as the file; don't bother patching
1337 # or a patch + rename cannot take place due to the target
1338 # directory not existing
1339 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001340 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001341 if sf.name in renames:
1342 del renames[sf.name]
1343 AddToKnownPaths(tf.name, known_paths)
1344 else:
1345 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1346 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1347 largest_source_size = max(largest_source_size, sf.size)
1348
1349 self.largest_source_size = largest_source_size
1350
1351 def EmitVerification(self, script):
1352 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001353 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001354 if tf.name != sf.name:
1355 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1356 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1357 so_far += sf.size
1358 return so_far
1359
Michael Runge63f01de2014-10-28 19:24:19 -07001360 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001361 for fn, _, sha1 in self.verbatim_targets:
1362 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001363 script.FileCheck("/"+fn, sha1)
1364 for tf, _, _, _ in self.patch_list:
1365 script.FileCheck(tf.name, tf.sha1)
1366
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001367 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001368 file_list = ["/" + i[0] for i in self.verbatim_targets]
1369 file_list += ["/" + i for i in self.source_data
1370 if i not in self.target_data and i not in self.renames]
1371 file_list += list(extras)
1372 # Sort the list in descending order, which removes all the files first
1373 # before attempting to remove the folder. (Bug: 22960996)
1374 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001375
1376 def TotalPatchSize(self):
1377 return sum(i[1].size for i in self.patch_list)
1378
1379 def EmitPatches(self, script, total_patch_size, so_far):
1380 self.deferred_patch_list = deferred_patch_list = []
1381 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001382 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001383 if tf.name == "system/build.prop":
1384 deferred_patch_list.append(item)
1385 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001386 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001387 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001388 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1389 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001390 so_far += tf.size
1391 script.SetProgress(so_far / total_patch_size)
1392 return so_far
1393
1394 def EmitDeferredPatches(self, script):
1395 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001396 tf, sf, _, _ = item
1397 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1398 "patch/" + sf.name + ".p")
1399 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001400
1401 def EmitRenames(self, script):
1402 if len(self.renames) > 0:
1403 script.Print("Renaming files...")
1404 for src, tgt in self.renames.iteritems():
1405 print "Renaming " + src + " to " + tgt.name
1406 script.RenameFile(src, tgt.name)
1407
1408
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001409def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001410 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1411 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1412
Doug Zongker26e66192014-02-20 13:22:07 -08001413 if (OPTIONS.block_based and
1414 target_has_recovery_patch and
1415 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001416 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1417
Doug Zongker37974732010-09-16 17:44:38 -07001418 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1419 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001420
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001421 if source_version == 0:
Tao Baoc0863702016-03-07 13:03:19 -08001422 print("WARNING: generating edify script for a source that "
1423 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001424 script = edify_generator.EdifyGenerator(
1425 source_version, OPTIONS.target_info_dict,
1426 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001427
Tao Bao34b47bf2015-06-22 19:17:41 -07001428 recovery_mount_options = OPTIONS.source_info_dict.get(
1429 "recovery_mount_options")
Tao Baoc0863702016-03-07 13:03:19 -08001430 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1431 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -07001432 oem_dict = None
Tao Baoc0863702016-03-07 13:03:19 -08001433 if source_oem_props or target_oem_props:
Michael Runge6e836112014-04-15 17:40:21 -07001434 if OPTIONS.oem_source is None:
1435 raise common.ExternalError("OEM source required for this build")
Tao Bao1bb5a182016-03-04 09:45:03 -08001436 if not OPTIONS.oem_no_mount:
1437 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001438 oem_dict = common.LoadDictionaryFromLines(
1439 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001440
Dan Albert8b72aef2015-03-23 19:13:21 -07001441 metadata = {
Tao Baoc0863702016-03-07 13:03:19 -08001442 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
1443 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001444 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001445 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001446
Tao Bao5d182562016-02-23 11:38:39 -08001447 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
1448 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1449 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1450
1451 if OPTIONS.downgrade:
1452 metadata["ota-downgrade"] = "yes"
1453 if not is_downgrade:
1454 raise RuntimeError("--downgrade specified but no downgrade detected: "
1455 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
1456 else:
1457 if is_downgrade:
1458 # Non-fatal here to allow generating such a package which may require
1459 # manual work to adjust the post-timestamp. A legit use case is that we
1460 # cut a new build C (after having A and B), but want to enfore the
1461 # update path of A -> C -> B. Specifying --downgrade may not help since
1462 # that would enforce a data wipe for C -> B update.
1463 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
1464 "The package may not be deployed properly. "
1465 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
1466 metadata["post-timestamp"] = post_timestamp
1467
Doug Zongker05d3dea2009-06-22 11:32:31 -07001468 device_specific = common.DeviceSpecificParams(
1469 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001470 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001471 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001472 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001473 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001474 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001475 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001476 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001477
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001478 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001479 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001480 if HasVendorPartition(target_zip):
1481 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001482 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001483 else:
1484 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001485
Tao Baoc0863702016-03-07 13:03:19 -08001486 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001487 OPTIONS.target_info_dict)
Tao Baoc0863702016-03-07 13:03:19 -08001488 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001489 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001490
Tao Baoc0863702016-03-07 13:03:19 -08001491 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001492 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Baoc0863702016-03-07 13:03:19 -08001493 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001494 script.AssertSomeThumbprint(
1495 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1496 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Baoc0863702016-03-07 13:03:19 -08001497 elif source_oem_props is None and target_oem_props is not None:
1498 script.AssertFingerprintOrThumbprint(
1499 source_fp,
1500 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1501 else:
1502 script.AssertFingerprintOrThumbprint(
1503 target_fp,
1504 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001505
Doug Zongker2ea21062010-04-28 16:05:21 -07001506 metadata["pre-build"] = source_fp
1507 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001508
Doug Zongker55d93282011-01-25 17:03:34 -08001509 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001510 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1511 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001512 target_boot = common.GetBootableImage(
1513 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001514 updating_boot = (not OPTIONS.two_step and
1515 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001516
Doug Zongker55d93282011-01-25 17:03:34 -08001517 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001518 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1519 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001520 target_recovery = common.GetBootableImage(
1521 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001522 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001523
Doug Zongker881dd402009-09-20 14:03:55 -07001524 # Here's how we divide up the progress bar:
1525 # 0.1 for verifying the start state (PatchCheck calls)
1526 # 0.8 for applying patches (ApplyPatch calls)
1527 # 0.1 for unpacking verbatim files, symlinking, and doing the
1528 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001529
Michael Runge6e836112014-04-15 17:40:21 -07001530 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001531 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001532
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001533 # Two-step incremental package strategy (in chronological order,
1534 # which is *not* the order in which the generated script has
1535 # things):
1536 #
1537 # if stage is not "2/3" or "3/3":
1538 # do verification on current system
1539 # write recovery image to boot partition
1540 # set stage to "2/3"
1541 # reboot to boot partition and restart recovery
1542 # else if stage is "2/3":
1543 # write recovery image to recovery partition
1544 # set stage to "3/3"
1545 # reboot to recovery partition and restart recovery
1546 # else:
1547 # (stage must be "3/3")
1548 # perform update:
1549 # patch system files, etc.
1550 # force full install of new boot image
1551 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001552 # complete script normally
1553 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001554
1555 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001556 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001557 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001558 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001559 assert fs.fs_type.upper() == "EMMC", \
1560 "two-step packages only supported on devices with EMMC /misc partitions"
1561 bcb_dev = {"bcb_dev": fs.device}
1562 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1563 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001564if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001565""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -07001566 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001567 script.WriteRawImage("/recovery", "recovery.img")
1568 script.AppendExtra("""
1569set_stage("%(bcb_dev)s", "3/3");
1570reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001571else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001572""" % bcb_dev)
1573
Tao Bao6c55a8a2015-04-08 15:30:27 -07001574 # Dump fingerprints
1575 script.Print("Source: %s" % (source_fp,))
1576 script.Print("Target: %s" % (target_fp,))
1577
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001578 script.Print("Verifying current system...")
1579
Doug Zongkere5ff5902012-01-17 10:55:37 -08001580 device_specific.IncrementalOTA_VerifyBegin()
1581
Doug Zongker881dd402009-09-20 14:03:55 -07001582 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001583 so_far = system_diff.EmitVerification(script)
1584 if vendor_diff:
1585 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001586
Tao Baod8d14be2016-02-04 14:26:02 -08001587 size = []
1588 if system_diff.patch_list:
1589 size.append(system_diff.largest_source_size)
1590 if vendor_diff:
1591 if vendor_diff.patch_list:
1592 size.append(vendor_diff.largest_source_size)
1593
Doug Zongker5da317e2009-06-02 13:38:17 -07001594 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001595 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001596 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001597 print "boot target: %d source: %d diff: %d" % (
1598 target_boot.size, source_boot.size, len(d))
1599
Doug Zongker048e7ca2009-06-15 14:31:53 -07001600 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001601
Tao Baodd24da92015-07-29 14:09:23 -07001602 boot_type, boot_device = common.GetTypeAndDevice(
1603 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001604
1605 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1606 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001607 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001608 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001609 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001610 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001611
Tao Baod8d14be2016-02-04 14:26:02 -08001612 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001613 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001614
Doug Zongker05d3dea2009-06-22 11:32:31 -07001615 device_specific.IncrementalOTA_VerifyEnd()
1616
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001617 if OPTIONS.two_step:
1618 script.WriteRawImage("/boot", "recovery.img")
1619 script.AppendExtra("""
1620set_stage("%(bcb_dev)s", "2/3");
1621reboot_now("%(bcb_dev)s", "");
1622else
1623""" % bcb_dev)
1624
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001625 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001626
Doug Zongkere5ff5902012-01-17 10:55:37 -08001627 device_specific.IncrementalOTA_InstallBegin()
1628
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001629 if OPTIONS.two_step:
1630 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1631 script.WriteRawImage("/boot", "boot.img")
1632 print "writing full boot image (forced by two-step mode)"
1633
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001634 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001635 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1636 if vendor_diff:
1637 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001638
Doug Zongker881dd402009-09-20 14:03:55 -07001639 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001640 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1641 if vendor_diff:
1642 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001643 if updating_boot:
1644 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001645
1646 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001647 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1648 if vendor_diff:
1649 script.Print("Patching vendor files...")
1650 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001651
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001652 if not OPTIONS.two_step:
1653 if updating_boot:
1654 # Produce the boot image by applying a patch to the current
1655 # contents of the boot partition, and write it back to the
1656 # partition.
1657 script.Print("Patching boot image...")
1658 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1659 % (boot_type, boot_device,
1660 source_boot.size, source_boot.sha1,
1661 target_boot.size, target_boot.sha1),
1662 "-",
1663 target_boot.size, target_boot.sha1,
1664 source_boot.sha1, "patch/boot.img.p")
1665 so_far += target_boot.size
1666 script.SetProgress(so_far / total_patch_size)
1667 print "boot image changed; including."
1668 else:
1669 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001670
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001671 system_items = ItemSet("system", "META/filesystem_config.txt")
1672 if vendor_diff:
1673 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1674
Doug Zongkereef39442009-04-02 12:14:19 -07001675 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001676 # Recovery is generated as a patch using both the boot image
1677 # (which contains the same linux kernel as recovery) and the file
1678 # /system/etc/recovery-resource.dat (which contains all the images
1679 # used in the recovery UI) as sources. This lets us minimize the
1680 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001681 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001682 # For older builds where recovery-resource.dat is not present, we
1683 # use only the boot image as the source.
1684
Doug Zongkerc9253822014-02-04 12:17:58 -08001685 if not target_has_recovery_patch:
1686 def output_sink(fn, data):
1687 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001688 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001689
1690 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1691 target_recovery, target_boot)
1692 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001693 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001694 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001695 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001696 else:
1697 print "recovery image unchanged; skipping."
1698
Doug Zongker881dd402009-09-20 14:03:55 -07001699 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001700
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001701 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1702 if vendor_diff:
1703 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1704
1705 temp_script = script.MakeTemporary()
1706 system_items.GetMetadata(target_zip)
1707 system_items.Get("system").SetPermissions(temp_script)
1708 if vendor_diff:
1709 vendor_items.GetMetadata(target_zip)
1710 vendor_items.Get("vendor").SetPermissions(temp_script)
1711
1712 # Note that this call will mess up the trees of Items, so make sure
1713 # we're done with them.
1714 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1715 if vendor_diff:
1716 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001717
1718 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001719 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1720
1721 # Delete all the symlinks in source that aren't in target. This
1722 # needs to happen before verbatim files are unpacked, in case a
1723 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001724
1725 # If a symlink in the source will be replaced by a regular file, we cannot
1726 # delete the symlink/file in case the package gets applied again. For such
1727 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1728 # (Bug: 23646151)
1729 replaced_symlinks = dict()
1730 if system_diff:
1731 for i in system_diff.verbatim_targets:
1732 replaced_symlinks["/%s" % (i[0],)] = i[2]
1733 if vendor_diff:
1734 for i in vendor_diff.verbatim_targets:
1735 replaced_symlinks["/%s" % (i[0],)] = i[2]
1736
1737 if system_diff:
1738 for tf in system_diff.renames.values():
1739 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1740 if vendor_diff:
1741 for tf in vendor_diff.renames.values():
1742 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1743
1744 always_delete = []
1745 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001746 for dest, link in source_symlinks:
1747 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001748 if link in replaced_symlinks:
1749 may_delete.append((link, replaced_symlinks[link]))
1750 else:
1751 always_delete.append(link)
1752 script.DeleteFiles(always_delete)
1753 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001754
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001755 if system_diff.verbatim_targets:
1756 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001757 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001758 if vendor_diff and vendor_diff.verbatim_targets:
1759 script.Print("Unpacking new vendor files...")
1760 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001761
Doug Zongkerc9253822014-02-04 12:17:58 -08001762 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001763 script.Print("Unpacking new recovery...")
1764 script.UnpackPackageDir("recovery", "/system")
1765
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001766 system_diff.EmitRenames(script)
1767 if vendor_diff:
1768 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001769
Doug Zongker05d3dea2009-06-22 11:32:31 -07001770 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001771
1772 # Create all the symlinks that don't already exist, or point to
1773 # somewhere different than what we want. Delete each symlink before
1774 # creating it, since the 'symlink' command won't overwrite.
1775 to_create = []
1776 for dest, link in target_symlinks:
1777 if link in source_symlinks_d:
1778 if dest != source_symlinks_d[link]:
1779 to_create.append((dest, link))
1780 else:
1781 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001782 script.DeleteFiles([i[1] for i in to_create])
1783 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001784
1785 # Now that the symlinks are created, we can set all the
1786 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001787 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001788
Doug Zongker881dd402009-09-20 14:03:55 -07001789 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001790 device_specific.IncrementalOTA_InstallEnd()
1791
Doug Zongker1c390a22009-05-14 19:06:36 -07001792 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001793 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001794
Doug Zongkere92f15a2011-08-26 13:46:40 -07001795 # Patch the build.prop file last, so if something fails but the
1796 # device can still come up, it appears to be the old build and will
1797 # get set the OTA package again to retry.
1798 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001799 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001800
Doug Zongker922206e2014-03-04 13:16:24 -08001801 if OPTIONS.wipe_user_data:
1802 script.Print("Erasing user data...")
1803 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001804 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001805
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001806 if OPTIONS.two_step:
1807 script.AppendExtra("""
1808set_stage("%(bcb_dev)s", "");
1809endif;
1810endif;
1811""" % bcb_dev)
1812
Michael Runge63f01de2014-10-28 19:24:19 -07001813 if OPTIONS.verify and system_diff:
1814 script.Print("Remounting and verifying system partition files...")
1815 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001816 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001817 system_diff.EmitExplicitTargetVerification(script)
1818
1819 if OPTIONS.verify and vendor_diff:
1820 script.Print("Remounting and verifying vendor partition files...")
1821 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001822 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001823 vendor_diff.EmitExplicitTargetVerification(script)
Doug Zongker25568482014-03-03 10:21:27 -08001824 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001825
Tao Baod8d14be2016-02-04 14:26:02 -08001826 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001827 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001828
1829
1830def main(argv):
1831
1832 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001833 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001834 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001835 elif o in ("-k", "--package_key"):
1836 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001837 elif o in ("-i", "--incremental_from"):
1838 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001839 elif o == "--full_radio":
1840 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001841 elif o == "--full_bootloader":
1842 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001843 elif o in ("-w", "--wipe_user_data"):
1844 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001845 elif o in ("-n", "--no_prereq"):
1846 OPTIONS.omit_prereq = True
Tao Bao5d182562016-02-23 11:38:39 -08001847 elif o == "--downgrade":
1848 OPTIONS.downgrade = True
1849 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07001850 elif o in ("-o", "--oem_settings"):
1851 OPTIONS.oem_source = a
Tao Bao8608cde2016-02-25 19:49:55 -08001852 elif o == "--oem_no_mount":
1853 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001854 elif o in ("-e", "--extra_script"):
1855 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001856 elif o in ("-a", "--aslr_mode"):
1857 if a in ("on", "On", "true", "True", "yes", "Yes"):
1858 OPTIONS.aslr_mode = True
1859 else:
1860 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001861 elif o in ("-t", "--worker_threads"):
1862 if a.isdigit():
1863 OPTIONS.worker_threads = int(a)
1864 else:
1865 raise ValueError("Cannot parse value %r for option %r - only "
1866 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001867 elif o in ("-2", "--two_step"):
1868 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001869 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001870 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001871 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001872 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001873 elif o == "--block":
1874 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001875 elif o in ("-b", "--binary"):
1876 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001877 elif o in ("--no_fallback_to_full",):
1878 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001879 elif o == "--stash_threshold":
1880 try:
1881 OPTIONS.stash_threshold = float(a)
1882 except ValueError:
1883 raise ValueError("Cannot parse value %r for option %r - expecting "
1884 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001885 elif o == "--gen_verify":
1886 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001887 elif o == "--log_diff":
1888 OPTIONS.log_diff = a
Doug Zongkereef39442009-04-02 12:14:19 -07001889 else:
1890 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001891 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001892
1893 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001894 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001895 extra_long_opts=[
1896 "board_config=",
1897 "package_key=",
1898 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001899 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001900 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001901 "wipe_user_data",
1902 "no_prereq",
Tao Bao5d182562016-02-23 11:38:39 -08001903 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07001904 "extra_script=",
1905 "worker_threads=",
1906 "aslr_mode=",
1907 "two_step",
1908 "no_signing",
1909 "block",
1910 "binary=",
1911 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001912 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001913 "verify",
1914 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001915 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001916 "gen_verify",
1917 "log_diff=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001918 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001919
1920 if len(args) != 2:
1921 common.Usage(__doc__)
1922 sys.exit(1)
1923
Tao Bao5d182562016-02-23 11:38:39 -08001924 if OPTIONS.downgrade:
1925 # Sanity check to enforce a data wipe.
1926 if not OPTIONS.wipe_user_data:
1927 raise ValueError("Cannot downgrade without a data wipe")
1928
1929 # We should only allow downgrading incrementals (as opposed to full).
1930 # Otherwise the device may go back from arbitrary build with this full
1931 # OTA package.
1932 if OPTIONS.incremental_source is None:
1933 raise ValueError("Cannot generate downgradable full OTAs - consider"
1934 "using --omit_prereq?")
1935
Tao Baoc098e9e2016-01-07 13:03:56 -08001936 # Load the dict file from the zip directly to have a peek at the OTA type.
1937 # For packages using A/B update, unzipping is not needed.
1938 input_zip = zipfile.ZipFile(args[0], "r")
1939 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1940 common.ZipClose(input_zip)
1941
1942 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1943
1944 if ab_update:
1945 if OPTIONS.incremental_source is not None:
1946 OPTIONS.target_info_dict = OPTIONS.info_dict
1947 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1948 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1949 common.ZipClose(source_zip)
1950
1951 if OPTIONS.verbose:
1952 print "--- target info ---"
1953 common.DumpInfoDict(OPTIONS.info_dict)
1954
1955 if OPTIONS.incremental_source is not None:
1956 print "--- source info ---"
1957 common.DumpInfoDict(OPTIONS.source_info_dict)
1958
1959 WriteABOTAPackageWithBrilloScript(
1960 target_file=args[0],
1961 output_file=args[1],
1962 source_file=OPTIONS.incremental_source)
1963
1964 print "done."
1965 return
1966
Doug Zongker1c390a22009-05-14 19:06:36 -07001967 if OPTIONS.extra_script is not None:
1968 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1969
Doug Zongkereef39442009-04-02 12:14:19 -07001970 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001971 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001972
Doug Zongkereef39442009-04-02 12:14:19 -07001973 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07001974 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001975
Doug Zongker37974732010-09-16 17:44:38 -07001976 if OPTIONS.verbose:
1977 print "--- target info ---"
1978 common.DumpInfoDict(OPTIONS.info_dict)
1979
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001980 # If the caller explicitly specified the device-specific extensions
1981 # path via -s/--device_specific, use that. Otherwise, use
1982 # META/releasetools.py if it is present in the target target_files.
1983 # Otherwise, take the path of the file from 'tool_extensions' in the
1984 # info dict and look for that in the local filesystem, relative to
1985 # the current directory.
1986
Doug Zongker37974732010-09-16 17:44:38 -07001987 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001988 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1989 if os.path.exists(from_input):
1990 print "(using device-specific extensions from target_files)"
1991 OPTIONS.device_specific = from_input
1992 else:
1993 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1994
Doug Zongker37974732010-09-16 17:44:38 -07001995 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001996 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001997
Tao Baoc098e9e2016-01-07 13:03:56 -08001998 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07001999 raise common.ExternalError(
2000 "--- target build has specified no recovery ---")
2001
Tao Bao767e3ac2015-11-10 12:19:19 -08002002 # Use the default key to sign the package if not specified with package_key.
2003 if not OPTIONS.no_signing:
2004 if OPTIONS.package_key is None:
2005 OPTIONS.package_key = OPTIONS.info_dict.get(
2006 "default_system_dev_certificate",
2007 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002008
Tao Bao767e3ac2015-11-10 12:19:19 -08002009 # Set up the output zip. Create a temporary zip file if signing is needed.
2010 if OPTIONS.no_signing:
2011 if os.path.exists(args[1]):
2012 os.unlink(args[1])
2013 output_zip = zipfile.ZipFile(args[1], "w",
2014 compression=zipfile.ZIP_DEFLATED)
2015 else:
2016 temp_zip_file = tempfile.NamedTemporaryFile()
2017 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2018 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002019
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002020 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002021 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002022 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002023 print "--- can't determine the cache partition size ---"
2024 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002025
Tao Bao9bc6bb22015-11-09 16:58:28 -08002026 # Generate a verify package.
2027 if OPTIONS.gen_verify:
2028 WriteVerifyPackage(input_zip, output_zip)
2029
Tao Bao767e3ac2015-11-10 12:19:19 -08002030 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002031 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002032 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002033
2034 # Generate an incremental OTA. It will fall back to generate a full OTA on
2035 # failure unless no_fallback_to_full is specified.
2036 else:
2037 print "unzipping source target-files..."
2038 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2039 OPTIONS.incremental_source)
2040 OPTIONS.target_info_dict = OPTIONS.info_dict
2041 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2042 OPTIONS.source_tmp)
2043 if OPTIONS.verbose:
2044 print "--- source info ---"
2045 common.DumpInfoDict(OPTIONS.source_info_dict)
2046 try:
2047 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002048 if OPTIONS.log_diff:
2049 out_file = open(OPTIONS.log_diff, 'w')
2050 import target_files_diff
2051 target_files_diff.recursiveDiff('',
2052 OPTIONS.source_tmp,
2053 OPTIONS.input_tmp,
2054 out_file)
2055 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002056 except ValueError:
2057 if not OPTIONS.fallback_to_full:
2058 raise
2059 print "--- failed to build incremental; falling back to full ---"
2060 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002061 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002062
Tao Bao767e3ac2015-11-10 12:19:19 -08002063 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002064
Tao Bao767e3ac2015-11-10 12:19:19 -08002065 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002066 if not OPTIONS.no_signing:
2067 SignOutput(temp_zip_file.name, args[1])
2068 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002069
Doug Zongkereef39442009-04-02 12:14:19 -07002070 print "done."
2071
2072
2073if __name__ == '__main__':
2074 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002075 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002076 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002077 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002078 print
2079 print " ERROR: %s" % (e,)
2080 print
2081 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002082 finally:
2083 common.Cleanup()