blob: 4b1b3a07ae41de86fae4f4fad84a25f33f8e4c23 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given a target-files zipfile, produces an OTA package that installs
19that build. An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage: ota_from_target_files [flags] input_target_files output_ota_package
23
Doug Zongker25568482014-03-03 10:21:27 -080024 --board_config <file>
Doug Zongkerfdd8e692009-08-03 17:27:48 -070025 Deprecated.
Doug Zongkereef39442009-04-02 12:14:19 -070026
Doug Zongkerafb32ea2011-09-22 10:28:04 -070027 -k (--package_key) <key> Key to use to sign the package (default is
28 the value of default_system_dev_certificate from the input
29 target-files's META/misc_info.txt, or
30 "build/target/product/security/testkey" if that value is not
31 specified).
32
33 For incremental OTAs, the default value is based on the source
34 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070035
36 -i (--incremental_from) <file>
37 Generate an incremental OTA using the given target-files zip as
38 the starting build.
39
Tao Bao43078aa2015-04-21 14:32:35 -070040 --full_radio
41 When generating an incremental OTA, always include a full copy of
42 radio image. This option is only meaningful when -i is specified,
43 because a full radio is always included in a full OTA if applicable.
44
leozwangaa6c1a12015-08-14 10:57:58 -070045 --full_bootloader
46 Similar to --full_radio. When generating an incremental OTA, always
47 include a full copy of bootloader image.
48
Michael Runge63f01de2014-10-28 19:24:19 -070049 -v (--verify)
50 Remount and verify the checksums of the files written to the
51 system and vendor (if used) partitions. Incremental builds only.
52
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080053 -o (--oem_settings) <main_file[,additional_files...]>
54 Comma seperated list of files used to specify the expected OEM-specific
55 properties on the OEM partition of the intended device.
56 Multiple expected values can be used by providing multiple files.
57
Michael Runge6e836112014-04-15 17:40:21 -070058
Tao Bao8608cde2016-02-25 19:49:55 -080059 --oem_no_mount
60 For devices with OEM-specific properties but without an OEM partition,
61 do not mount the OEM partition in the updater-script. This should be
62 very rarely used, since it's expected to have a dedicated OEM partition
63 for OEM-specific properties. Only meaningful when -o is specified.
64
Doug Zongkerdbfaae52009-04-21 17:12:54 -070065 -w (--wipe_user_data)
66 Generate an OTA package that will wipe the user data partition
67 when installed.
68
Tao Bao5d182562016-02-23 11:38:39 -080069 --downgrade
70 Intentionally generate an incremental OTA that updates from a newer
71 build to an older one (based on timestamp comparison). "post-timestamp"
72 will be replaced by "ota-downgrade=yes" in the metadata file. A data
73 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080074 the metadata file. The update-binary in the source build will be used in
75 the OTA package, unless --binary flag is specified.
Tao Bao5d182562016-02-23 11:38:39 -080076
Doug Zongker1c390a22009-05-14 19:06:36 -070077 -e (--extra_script) <file>
78 Insert the contents of file at the end of the update script.
79
Doug Zongker9b23f2c2013-11-25 14:44:12 -080080 -2 (--two_step)
81 Generate a 'two-step' OTA package, where recovery is updated
82 first, so that any changes made to the system partition are done
83 using the new recovery (new kernel, etc.).
84
Doug Zongker26e66192014-02-20 13:22:07 -080085 --block
86 Generate a block-based OTA if possible. Will fall back to a
87 file-based OTA if the target_files is older and doesn't support
88 block-based OTAs.
89
Doug Zongker25568482014-03-03 10:21:27 -080090 -b (--binary) <file>
91 Use the given binary as the update-binary in the output package,
92 instead of the binary in the build's target_files. Use for
93 development only.
94
Martin Blumenstingl374e1142014-05-31 20:42:55 +020095 -t (--worker_threads) <int>
96 Specifies the number of worker-threads that will be used when
97 generating patches for incremental updates (defaults to 3).
98
Tao Bao8dcf7382015-05-21 14:09:49 -070099 --stash_threshold <float>
100 Specifies the threshold that will be used to compute the maximum
101 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800102
103 --gen_verify
104 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800105
106 --log_diff <file>
107 Generate a log file that shows the differences in the source and target
108 builds for an incremental package. This option is only meaningful when
109 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700110
111 --payload_signer <signer>
112 Specify the signer when signing the payload and metadata for A/B OTAs.
113 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
114 with the package private key. If the private key cannot be accessed
115 directly, a payload signer that knows how to do that should be specified.
116 The signer will be supplied with "-inkey <path_to_key>",
117 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700118
119 --payload_signer_args <args>
120 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700121"""
122
Tao Bao89fbb0f2017-01-10 10:47:58 -0800123from __future__ import print_function
124
Doug Zongkereef39442009-04-02 12:14:19 -0700125import sys
126
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800127if sys.hexversion < 0x02070000:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800128 print("Python 2.7 or newer is required.", file=sys.stderr)
Doug Zongkereef39442009-04-02 12:14:19 -0700129 sys.exit(1)
130
Tao Bao2dd1c482017-02-03 16:49:39 -0800131import copy
Doug Zongkerfc44a512014-08-26 13:10:25 -0700132import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800133import os.path
Tao Baoc098e9e2016-01-07 13:03:56 -0800134import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700135import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700136import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700137import zipfile
138
139import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700140import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700141import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700142
143OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700144OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700145OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700146OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700147OPTIONS.require_verbatim = set()
148OPTIONS.prohibit_verbatim = set(("system/build.prop",))
149OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700150OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800151OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700152OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700153OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
154if OPTIONS.worker_threads == 0:
155 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800156OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900157OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800158OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800159OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700160OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800161OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700162OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700163OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700164OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700165# Stash size cannot exceed cache_size * threshold.
166OPTIONS.cache_size = None
167OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800168OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800169OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700170OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700171OPTIONS.payload_signer_args = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700172
Tao Bao2dd1c482017-02-03 16:49:39 -0800173METADATA_NAME = 'META-INF/com/android/metadata'
174
Doug Zongkereef39442009-04-02 12:14:19 -0700175def MostPopularKey(d, default):
176 """Given a dict, return the key corresponding to the largest
177 value. Returns 'default' if the dict is empty."""
178 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700179 if not x:
180 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700181 x.sort()
182 return x[-1][1]
183
184
185def IsSymlink(info):
186 """Return true if the zipfile.ZipInfo object passed in represents a
187 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700188 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700189
Hristo Bojinov96be7202010-08-02 10:26:17 -0700190def IsRegular(info):
191 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700192 regular file."""
193 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700194
Michael Runge4038aa82013-12-13 18:06:28 -0800195def ClosestFileMatch(src, tgtfiles, existing):
196 """Returns the closest file match between a source file and list
197 of potential matches. The exact filename match is preferred,
198 then the sha1 is searched for, and finally a file with the same
199 basename is evaluated. Rename support in the updater-binary is
200 required for the latter checks to be used."""
201
202 result = tgtfiles.get("path:" + src.name)
203 if result is not None:
204 return result
205
206 if not OPTIONS.target_info_dict.get("update_rename_support", False):
207 return None
208
209 if src.size < 1000:
210 return None
211
212 result = tgtfiles.get("sha1:" + src.sha1)
213 if result is not None and existing.get(result.name) is None:
214 return result
215 result = tgtfiles.get("file:" + src.name.split("/")[-1])
216 if result is not None and existing.get(result.name) is None:
217 return result
218 return None
219
Dan Albert8b72aef2015-03-23 19:13:21 -0700220class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700221 def __init__(self, partition, fs_config):
222 self.partition = partition
223 self.fs_config = fs_config
224 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700225
Dan Albert8b72aef2015-03-23 19:13:21 -0700226 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700227 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700228 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700229 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700230
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700231 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700232 # The target_files contains a record of what the uid,
233 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700234 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700235
236 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700237 if not line:
238 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700239 columns = line.split()
240 name, uid, gid, mode = columns[:4]
241 selabel = None
242 capabilities = None
243
244 # After the first 4 columns, there are a series of key=value
245 # pairs. Extract out the fields we care about.
246 for element in columns[4:]:
247 key, value = element.split("=")
248 if key == "selabel":
249 selabel = value
250 if key == "capabilities":
251 capabilities = value
252
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700253 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700254 if i is not None:
255 i.uid = int(uid)
256 i.gid = int(gid)
257 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700258 i.selabel = selabel
259 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700260 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700261 i.children.sort(key=lambda i: i.name)
262
Tao Baof2cffbd2015-07-22 12:33:18 -0700263 # Set metadata for the files generated by this script. For full recovery
264 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700265 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700266 if i:
267 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700268 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700269 if i:
270 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700271
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700272
Dan Albert8b72aef2015-03-23 19:13:21 -0700273class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700274 """Items represent the metadata (user, group, mode) of files and
275 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700276 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700277 self.itemset = itemset
278 self.name = name
279 self.uid = None
280 self.gid = None
281 self.mode = None
282 self.selabel = None
283 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700284 self.is_dir = is_dir
285 self.descendants = None
286 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700287
288 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700289 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700290 self.parent.children.append(self)
291 else:
292 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700293 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700294 self.children = []
295
296 def Dump(self, indent=0):
297 if self.uid is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800298 print("%s%s %d %d %o" % (
299 " " * indent, self.name, self.uid, self.gid, self.mode))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700300 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800301 print("%s%s %s %s %s" % (
302 " " * indent, self.name, self.uid, self.gid, self.mode))
Dan Albert8b72aef2015-03-23 19:13:21 -0700303 if self.is_dir:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800304 print("%s%s" % (" " * indent, self.descendants))
305 print("%s%s" % (" " * indent, self.best_subtree))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700306 for i in self.children:
307 i.Dump(indent=indent+1)
308
Doug Zongkereef39442009-04-02 12:14:19 -0700309 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700310 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700311 all children and determine the best strategy for using set_perm_recursive
312 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700313 values. Recursively calls itself for all descendants.
314
Dan Albert8b72aef2015-03-23 19:13:21 -0700315 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
316 counting up all descendants of this node. (dmode or fmode may be None.)
317 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
318 fmode, selabel, capabilities) tuple that will match the most descendants of
319 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700320 """
321
Dan Albert8b72aef2015-03-23 19:13:21 -0700322 assert self.is_dir
323 key = (self.uid, self.gid, self.mode, None, self.selabel,
324 self.capabilities)
325 self.descendants = {key: 1}
326 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700327 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700328 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700329 for k, v in i.CountChildMetadata().iteritems():
330 d[k] = d.get(k, 0) + v
331 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700332 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700333 d[k] = d.get(k, 0) + 1
334
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700335 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
336 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700337
338 # First, find the (uid, gid) pair that matches the most
339 # descendants.
340 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700341 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700342 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
343 ug = MostPopularKey(ug, (0, 0))
344
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700345 # Now find the dmode, fmode, selabel, and capabilities that match
346 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700347 best_dmode = (0, 0o755)
348 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700349 best_selabel = (0, None)
350 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700351 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700352 if k[:2] != ug:
353 continue
354 if k[2] is not None and count >= best_dmode[0]:
355 best_dmode = (count, k[2])
356 if k[3] is not None and count >= best_fmode[0]:
357 best_fmode = (count, k[3])
358 if k[4] is not None and count >= best_selabel[0]:
359 best_selabel = (count, k[4])
360 if k[5] is not None and count >= best_capabilities[0]:
361 best_capabilities = (count, k[5])
362 self.best_subtree = ug + (
363 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700364
365 return d
366
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700367 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700368 """Append set_perm/set_perm_recursive commands to 'script' to
369 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700370 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700371
372 self.CountChildMetadata()
373
374 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700375 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
376 # that the current item (and all its children) have already been set to.
377 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700378 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700379 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700380 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700381 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700382 current = item.best_subtree
383
384 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700385 item.mode != current[2] or item.selabel != current[4] or \
386 item.capabilities != current[5]:
387 script.SetPermissions("/"+item.name, item.uid, item.gid,
388 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700389
390 for i in item.children:
391 recurse(i, current)
392 else:
393 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700394 item.mode != current[3] or item.selabel != current[4] or \
395 item.capabilities != current[5]:
396 script.SetPermissions("/"+item.name, item.uid, item.gid,
397 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700398
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700399 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700400
401
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700402def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
403 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700404 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800405 list of symlinks. output_zip may be None, in which case the copy is
406 skipped (but the other side effects still happen). substitute is an
407 optional dict of {output filename: contents} to be output instead of
408 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700409 """
410
411 symlinks = []
412
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700413 partition = itemset.partition
414
Doug Zongkereef39442009-04-02 12:14:19 -0700415 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700416 prefix = partition.upper() + "/"
417 if info.filename.startswith(prefix):
418 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700419 if IsSymlink(info):
420 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700421 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700422 else:
423 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700424 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700425 if substitute and fn in substitute and substitute[fn] is None:
426 continue
427 if output_zip is not None:
428 if substitute and fn in substitute:
429 data = substitute[fn]
430 else:
431 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700432 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700433 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700434 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700435 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700436 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700437
438 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800439 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700440
441
Doug Zongkereef39442009-04-02 12:14:19 -0700442def SignOutput(temp_zip_name, output_zip_name):
443 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
444 pw = key_passwords[OPTIONS.package_key]
445
Doug Zongker951495f2009-08-14 12:44:19 -0700446 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
447 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700448
449
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800450def AppendAssertions(script, info_dict, oem_dicts=None):
Michael Runge6e836112014-04-15 17:40:21 -0700451 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700452 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700453 device = GetBuildProp("ro.product.device", info_dict)
454 script.AssertDevice(device)
455 else:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800456 if not oem_dicts:
Dan Albert8b72aef2015-03-23 19:13:21 -0700457 raise common.ExternalError(
458 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700459 for prop in oem_props.split():
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800460 values = []
461 for oem_dict in oem_dicts:
462 if oem_dict.get(prop):
463 values.append(oem_dict[prop])
464 if not values:
Dan Albert8b72aef2015-03-23 19:13:21 -0700465 raise common.ExternalError(
466 "The OEM file is missing the property %s" % prop)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800467 script.AssertOemProperty(prop, values)
468
469
470def _LoadOemDicts(script, recovery_mount_options):
471 """Returns the list of loaded OEM properties dict."""
472 oem_dicts = None
473 if OPTIONS.oem_source is None:
474 raise common.ExternalError("OEM source required for this build")
475 if not OPTIONS.oem_no_mount:
476 script.Mount("/oem", recovery_mount_options)
477 oem_dicts = []
478 for oem_file in OPTIONS.oem_source:
479 oem_dicts.append(common.LoadDictionaryFromLines(
480 open(oem_file).readlines()))
481 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700482
Doug Zongkereef39442009-04-02 12:14:19 -0700483
Tao Baod42e97e2016-11-30 12:11:57 -0800484def _WriteRecoveryImageToBoot(script, output_zip):
485 """Find and write recovery image to /boot in two-step OTA.
486
487 In two-step OTAs, we write recovery image to /boot as the first step so that
488 we can reboot to there and install a new recovery image to /recovery.
489 A special "recovery-two-step.img" will be preferred, which encodes the correct
490 path of "/boot". Otherwise the device may show "device is corrupt" message
491 when booting into /boot.
492
493 Fall back to using the regular recovery.img if the two-step recovery image
494 doesn't exist. Note that rebuilding the special image at this point may be
495 infeasible, because we don't have the desired boot signer and keys when
496 calling ota_from_target_files.py.
497 """
498
499 recovery_two_step_img_name = "recovery-two-step.img"
500 recovery_two_step_img_path = os.path.join(
501 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
502 if os.path.exists(recovery_two_step_img_path):
503 recovery_two_step_img = common.GetBootableImage(
504 recovery_two_step_img_name, recovery_two_step_img_name,
505 OPTIONS.input_tmp, "RECOVERY")
506 common.ZipWriteStr(
507 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800508 print("two-step package: using %s in stage 1/3" % (
509 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800510 script.WriteRawImage("/boot", recovery_two_step_img_name)
511 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800512 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800513 # The "recovery.img" entry has been written into package earlier.
514 script.WriteRawImage("/boot", "recovery.img")
515
516
Doug Zongkerc9253822014-02-04 12:17:58 -0800517def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700518 namelist = [name for name in target_files_zip.namelist()]
519 return ("SYSTEM/recovery-from-boot.p" in namelist or
520 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700521
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700522def HasVendorPartition(target_files_zip):
523 try:
524 target_files_zip.getinfo("VENDOR/")
525 return True
526 except KeyError:
527 return False
528
Michael Runge6e836112014-04-15 17:40:21 -0700529def GetOemProperty(name, oem_props, oem_dict, info_dict):
530 if oem_props is not None and name in oem_props:
531 return oem_dict[name]
532 return GetBuildProp(name, info_dict)
533
534
535def CalculateFingerprint(oem_props, oem_dict, info_dict):
536 if oem_props is None:
537 return GetBuildProp("ro.build.fingerprint", info_dict)
538 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700539 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
540 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
541 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
542 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700543
Doug Zongkerfc44a512014-08-26 13:10:25 -0700544
Doug Zongker3c84f562014-07-31 11:06:30 -0700545def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700546 # Return an image object (suitable for passing to BlockImageDiff)
547 # for the 'which' partition (most be "system" or "vendor"). If a
548 # prebuilt image and file map are found in tmpdir they are used,
549 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700550
551 assert which in ("system", "vendor")
552
553 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700554 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
555 if os.path.exists(path) and os.path.exists(mappath):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800556 print("using %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700557 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700558
559 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800560 print("building %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700561
562 # This is an 'old' target-files, which does not contain images
563 # already built. Build them.
564
Doug Zongkerfc44a512014-08-26 13:10:25 -0700565 mappath = tempfile.mkstemp()[1]
566 OPTIONS.tempfiles.append(mappath)
567
Doug Zongker3c84f562014-07-31 11:06:30 -0700568 import add_img_to_target_files
569 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700570 path = add_img_to_target_files.BuildSystem(
571 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700572 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700573 path = add_img_to_target_files.BuildVendor(
574 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700575
Tao Baoff777812015-05-12 11:42:31 -0700576 # Bug: http://b/20939131
577 # In ext4 filesystems, block 0 might be changed even being mounted
578 # R/O. We add it to clobbered_blocks so that it will be written to the
579 # target unconditionally. Note that they are still part of care_map.
580 clobbered_blocks = "0"
581
582 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700583
584
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700585def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700586 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700587 # be installed on top of. For now, we expect the API just won't
588 # change very often. Similarly for fstab, it might have changed
589 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700590 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700591
Tao Bao838c68f2016-03-15 19:16:18 +0000592 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700593 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800594 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700595 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800596 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -0700597
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800598 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
599 OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700600 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800601 "pre-device": GetOemProperty("ro.product.device", oem_props,
602 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -0700603 OPTIONS.info_dict),
604 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
605 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700606
Doug Zongker05d3dea2009-06-22 11:32:31 -0700607 device_specific = common.DeviceSpecificParams(
608 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700609 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700610 output_zip=output_zip,
611 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700612 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700613 metadata=metadata,
614 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700615
Doug Zongkerc9253822014-02-04 12:17:58 -0800616 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800617 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800618
Tao Baod8d14be2016-02-04 14:26:02 -0800619 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
620
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700621 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
622 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
623 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700624
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800625 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700626 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800627
628 # Two-step package strategy (in chronological order, which is *not*
629 # the order in which the generated script has things):
630 #
631 # if stage is not "2/3" or "3/3":
632 # write recovery image to boot partition
633 # set stage to "2/3"
634 # reboot to boot partition and restart recovery
635 # else if stage is "2/3":
636 # write recovery image to recovery partition
637 # set stage to "3/3"
638 # reboot to recovery partition and restart recovery
639 # else:
640 # (stage must be "3/3")
641 # set stage to ""
642 # do normal full package installation:
643 # wipe and install system, boot image, etc.
644 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700645 # complete script normally
646 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800647
648 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
649 OPTIONS.input_tmp, "RECOVERY")
650 if OPTIONS.two_step:
651 if not OPTIONS.info_dict.get("multistage_support", None):
652 assert False, "two-step packages not supported by this build"
653 fs = OPTIONS.info_dict["fstab"]["/misc"]
654 assert fs.fs_type.upper() == "EMMC", \
655 "two-step packages only supported on devices with EMMC /misc partitions"
656 bcb_dev = {"bcb_dev": fs.device}
657 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
658 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700659if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800660""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800661
662 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
663 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800664 script.WriteRawImage("/recovery", "recovery.img")
665 script.AppendExtra("""
666set_stage("%(bcb_dev)s", "3/3");
667reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700668else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800669""" % bcb_dev)
670
Tao Baod42e97e2016-11-30 12:11:57 -0800671 # Stage 3/3: Make changes.
672 script.Comment("Stage 3/3")
673
Tao Bao6c55a8a2015-04-08 15:30:27 -0700674 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700675 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700676
Doug Zongkere5ff5902012-01-17 10:55:37 -0800677 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700678
Doug Zongker01ce19c2014-02-04 13:48:15 -0800679 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700680
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700681 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800682 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700683 if HasVendorPartition(input_zip):
684 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700685
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400686 # Place a copy of file_contexts.bin into the OTA package which will be used
687 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700688 if "selinux_fc" in OPTIONS.info_dict:
689 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500690
Michael Runge7cd99ba2014-10-22 17:21:48 -0700691 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
692
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700693 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700694 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800695
Doug Zongker26e66192014-02-20 13:22:07 -0800696 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700697 # Full OTA is done as an "incremental" against an empty source
698 # image. This has the effect of writing new data from the package
699 # to the entire partition, but lets us reuse the updater code that
700 # writes incrementals to do it.
701 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
702 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700703 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700704 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800705 else:
706 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700707 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800708 if not has_recovery_patch:
709 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800710 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700711
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700712 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800713 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700714
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700715 boot_img = common.GetBootableImage(
716 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800717
Doug Zongker91a99c22014-05-09 13:15:01 -0700718 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800719 def output_sink(fn, data):
720 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700721 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800722
723 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
724 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700725
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700726 system_items.GetMetadata(input_zip)
727 system_items.Get("system").SetPermissions(script)
728
729 if HasVendorPartition(input_zip):
730 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
731 script.ShowProgress(0.1, 0)
732
733 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700734 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
735 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700736 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700737 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700738 else:
739 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700740 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700741 script.UnpackPackageDir("vendor", "/vendor")
742
743 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
744 script.MakeSymlinks(symlinks)
745
746 vendor_items.GetMetadata(input_zip)
747 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700748
Doug Zongker37974732010-09-16 17:44:38 -0700749 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700750 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700751
Doug Zongker01ce19c2014-02-04 13:48:15 -0800752 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700753 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700754
Doug Zongker01ce19c2014-02-04 13:48:15 -0800755 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700756 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700757
Doug Zongker1c390a22009-05-14 19:06:36 -0700758 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700759 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700760
Doug Zongker14833602010-02-02 13:12:04 -0800761 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800762
Doug Zongker922206e2014-03-04 13:16:24 -0800763 if OPTIONS.wipe_user_data:
764 script.ShowProgress(0.1, 10)
765 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700766
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800767 if OPTIONS.two_step:
768 script.AppendExtra("""
769set_stage("%(bcb_dev)s", "");
770""" % bcb_dev)
771 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800772
773 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
774 script.Comment("Stage 1/3")
775 _WriteRecoveryImageToBoot(script, output_zip)
776
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800777 script.AppendExtra("""
778set_stage("%(bcb_dev)s", "2/3");
779reboot_now("%(bcb_dev)s", "");
780endif;
781endif;
782""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800783
Tao Bao5d182562016-02-23 11:38:39 -0800784 script.SetProgress(1)
785 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800786 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700787 WriteMetadata(metadata, output_zip)
788
Doug Zongkerfc44a512014-08-26 13:10:25 -0700789
Dan Albert8e0178d2015-01-27 15:53:15 -0800790def WritePolicyConfig(file_name, output_zip):
791 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500792
Doug Zongker2ea21062010-04-28 16:05:21 -0700793
794def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800795 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
796 common.ZipWriteStr(output_zip, METADATA_NAME, value,
797 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700798
Doug Zongkerfc44a512014-08-26 13:10:25 -0700799
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700800def LoadPartitionFiles(z, partition):
801 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700802 ZipFile, and return a dict of {filename: File object}."""
803 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700804 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700805 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700806 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700807 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700808 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700809 data = z.read(info.filename)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +0900810 out[fn] = common.File(fn, data, info.compress_size)
Doug Zongker1807e702012-02-28 12:21:08 -0800811 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700812
813
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700814def GetBuildProp(prop, info_dict):
815 """Return the fingerprint of the build of a given target-files info_dict."""
816 try:
817 return info_dict.get("build.prop", {})[prop]
818 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700819 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700820
Doug Zongkerfc44a512014-08-26 13:10:25 -0700821
Michael Runge4038aa82013-12-13 18:06:28 -0800822def AddToKnownPaths(filename, known_paths):
823 if filename[-1] == "/":
824 return
825 dirs = filename.split("/")[:-1]
826 while len(dirs) > 0:
827 path = "/".join(dirs)
828 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700829 break
Michael Runge4038aa82013-12-13 18:06:28 -0800830 known_paths.add(path)
831 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700832
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700833
Tao Baob31892e2017-02-07 11:21:17 -0800834def HandleDowngradeMetadata(metadata):
835 # Only incremental OTAs are allowed to reach here.
836 assert OPTIONS.incremental_source is not None
837
838 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
839 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
840 is_downgrade = long(post_timestamp) < long(pre_timestamp)
841
842 if OPTIONS.downgrade:
843 metadata["ota-downgrade"] = "yes"
844 if not is_downgrade:
845 raise RuntimeError("--downgrade specified but no downgrade detected: "
846 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
847 else:
848 if is_downgrade:
849 # Non-fatal here to allow generating such a package which may require
850 # manual work to adjust the post-timestamp. A legit use case is that we
851 # cut a new build C (after having A and B), but want to enfore the
852 # update path of A -> C -> B. Specifying --downgrade may not help since
853 # that would enforce a data wipe for C -> B update.
854 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
855 "The package may not be deployed properly. "
856 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
857 metadata["post-timestamp"] = post_timestamp
858
859
Geremy Condra36bd3652014-02-06 19:45:10 -0800860def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700861 # TODO(tbao): We should factor out the common parts between
862 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800863 source_version = OPTIONS.source_info_dict["recovery_api_version"]
864 target_version = OPTIONS.target_info_dict["recovery_api_version"]
865
866 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700867 print("WARNING: generating edify script for a source that "
868 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700869 script = edify_generator.EdifyGenerator(
870 source_version, OPTIONS.target_info_dict,
871 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800872
Tao Bao3806c232015-07-05 21:08:33 -0700873 recovery_mount_options = OPTIONS.source_info_dict.get(
874 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700875 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
876 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800877 oem_dicts = None
878 if source_oem_props and target_oem_props:
879 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700880
Dan Albert8b72aef2015-03-23 19:13:21 -0700881 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700882 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800883 oem_dicts and oem_dicts[0],
884 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800885 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700886 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800887
Tao Baob31892e2017-02-07 11:21:17 -0800888 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800889
Geremy Condra36bd3652014-02-06 19:45:10 -0800890 device_specific = common.DeviceSpecificParams(
891 source_zip=source_zip,
892 source_version=source_version,
893 target_zip=target_zip,
894 target_version=target_version,
895 output_zip=output_zip,
896 script=script,
897 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700898 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800899
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800900 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700901 OPTIONS.source_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800902 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700903 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800904 metadata["pre-build"] = source_fp
905 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700906 metadata["pre-build-incremental"] = GetBuildProp(
907 "ro.build.version.incremental", OPTIONS.source_info_dict)
908 metadata["post-build-incremental"] = GetBuildProp(
909 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800910
911 source_boot = common.GetBootableImage(
912 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
913 OPTIONS.source_info_dict)
914 target_boot = common.GetBootableImage(
915 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
916 updating_boot = (not OPTIONS.two_step and
917 (source_boot.data != target_boot.data))
918
Geremy Condra36bd3652014-02-06 19:45:10 -0800919 target_recovery = common.GetBootableImage(
920 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800921
Doug Zongkerfc44a512014-08-26 13:10:25 -0700922 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
923 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700924
925 blockimgdiff_version = 1
926 if OPTIONS.info_dict:
927 blockimgdiff_version = max(
928 int(i) for i in
929 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
930
Tao Baof8acad12016-07-07 09:09:58 -0700931 # Check the first block of the source system partition for remount R/W only
932 # if the filesystem is ext4.
933 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
934 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700935 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
936 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
937 # b) the blocks listed in block map may not contain all the bytes for a given
938 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700939 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
940 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
941 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700942 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800943 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700944 version=blockimgdiff_version,
945 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700946
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700947 if HasVendorPartition(target_zip):
948 if not HasVendorPartition(source_zip):
949 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700950 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
951 OPTIONS.source_info_dict)
952 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
953 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800954
955 # Check first block of vendor partition for remount R/W only if
956 # disk type is ext4
957 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800958 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700959 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700960 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800961 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700962 version=blockimgdiff_version,
963 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700964 else:
965 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800966
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800967 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800968 device_specific.IncrementalOTA_Assertions()
969
970 # Two-step incremental package strategy (in chronological order,
971 # which is *not* the order in which the generated script has
972 # things):
973 #
974 # if stage is not "2/3" or "3/3":
975 # do verification on current system
976 # write recovery image to boot partition
977 # set stage to "2/3"
978 # reboot to boot partition and restart recovery
979 # else if stage is "2/3":
980 # write recovery image to recovery partition
981 # set stage to "3/3"
982 # reboot to recovery partition and restart recovery
983 # else:
984 # (stage must be "3/3")
985 # perform update:
986 # patch system files, etc.
987 # force full install of new boot image
988 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700989 # complete script normally
990 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800991
992 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700993 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800994 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700995 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800996 assert fs.fs_type.upper() == "EMMC", \
997 "two-step packages only supported on devices with EMMC /misc partitions"
998 bcb_dev = {"bcb_dev": fs.device}
999 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1000 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001001if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001002""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001003
1004 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1005 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001006 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001007 script.WriteRawImage("/recovery", "recovery.img")
1008 script.AppendExtra("""
1009set_stage("%(bcb_dev)s", "3/3");
1010reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001011else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001012""" % bcb_dev)
1013
Tao Baod42e97e2016-11-30 12:11:57 -08001014 # Stage 1/3: (a) Verify the current system.
1015 script.Comment("Stage 1/3")
1016
Tao Bao6c55a8a2015-04-08 15:30:27 -07001017 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -08001018 script.Print("Source: %s" % (source_fp,))
1019 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001020
Geremy Condra36bd3652014-02-06 19:45:10 -08001021 script.Print("Verifying current system...")
1022
1023 device_specific.IncrementalOTA_VerifyBegin()
1024
Tao Bao3e30d972016-03-15 13:20:19 -07001025 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1026 # patching on a device that's already on the target build will damage the
1027 # system. Because operations like move don't check the block state, they
1028 # always apply the changes unconditionally.
1029 if blockimgdiff_version <= 2:
1030 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001031 script.AssertSomeFingerprint(source_fp)
1032 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001033 script.AssertSomeThumbprint(
1034 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001035
1036 else: # blockimgdiff_version > 2
1037 if source_oem_props is None and target_oem_props is None:
1038 script.AssertSomeFingerprint(source_fp, target_fp)
1039 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001040 script.AssertSomeThumbprint(
1041 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1042 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001043 elif source_oem_props is None and target_oem_props is not None:
1044 script.AssertFingerprintOrThumbprint(
1045 source_fp,
1046 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1047 else:
1048 script.AssertFingerprintOrThumbprint(
1049 target_fp,
1050 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001051
Tao Baod8d14be2016-02-04 14:26:02 -08001052 # Check the required cache size (i.e. stashed blocks).
1053 size = []
1054 if system_diff:
1055 size.append(system_diff.required_cache)
1056 if vendor_diff:
1057 size.append(vendor_diff.required_cache)
1058
Geremy Condra36bd3652014-02-06 19:45:10 -08001059 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001060 boot_type, boot_device = common.GetTypeAndDevice(
1061 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001062 d = common.Difference(target_boot, source_boot)
1063 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001064 if d is None:
1065 include_full_boot = True
1066 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1067 else:
1068 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001069
Tao Bao89fbb0f2017-01-10 10:47:58 -08001070 print("boot target: %d source: %d diff: %d" % (
1071 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001072
Doug Zongkerf8340082014-08-05 10:39:37 -07001073 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001074
Doug Zongkerf8340082014-08-05 10:39:37 -07001075 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1076 (boot_type, boot_device,
1077 source_boot.size, source_boot.sha1,
1078 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001079 size.append(target_boot.size)
1080
1081 if size:
1082 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001083
1084 device_specific.IncrementalOTA_VerifyEnd()
1085
1086 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001087 # Stage 1/3: (b) Write recovery image to /boot.
1088 _WriteRecoveryImageToBoot(script, output_zip)
1089
Geremy Condra36bd3652014-02-06 19:45:10 -08001090 script.AppendExtra("""
1091set_stage("%(bcb_dev)s", "2/3");
1092reboot_now("%(bcb_dev)s", "");
1093else
1094""" % bcb_dev)
1095
Tao Baod42e97e2016-11-30 12:11:57 -08001096 # Stage 3/3: Make changes.
1097 script.Comment("Stage 3/3")
1098
Jesse Zhao75bcea02015-01-06 10:59:53 -08001099 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001100 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001101 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001102 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001103
Geremy Condra36bd3652014-02-06 19:45:10 -08001104 script.Comment("---- start making changes here ----")
1105
1106 device_specific.IncrementalOTA_InstallBegin()
1107
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001108 system_diff.WriteScript(script, output_zip,
1109 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001110
Doug Zongkerfc44a512014-08-26 13:10:25 -07001111 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001112 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001113
1114 if OPTIONS.two_step:
1115 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1116 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001117 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001118
1119 if not OPTIONS.two_step:
1120 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001121 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001122 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001123 script.Print("Installing boot image...")
1124 script.WriteRawImage("/boot", "boot.img")
1125 else:
1126 # Produce the boot image by applying a patch to the current
1127 # contents of the boot partition, and write it back to the
1128 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001129 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001130 script.Print("Patching boot image...")
1131 script.ShowProgress(0.1, 10)
1132 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1133 % (boot_type, boot_device,
1134 source_boot.size, source_boot.sha1,
1135 target_boot.size, target_boot.sha1),
1136 "-",
1137 target_boot.size, target_boot.sha1,
1138 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001139 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001140 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001141
1142 # Do device-specific installation (eg, write radio image).
1143 device_specific.IncrementalOTA_InstallEnd()
1144
1145 if OPTIONS.extra_script is not None:
1146 script.AppendExtra(OPTIONS.extra_script)
1147
Doug Zongker922206e2014-03-04 13:16:24 -08001148 if OPTIONS.wipe_user_data:
1149 script.Print("Erasing user data...")
1150 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001151 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001152
Geremy Condra36bd3652014-02-06 19:45:10 -08001153 if OPTIONS.two_step:
1154 script.AppendExtra("""
1155set_stage("%(bcb_dev)s", "");
1156endif;
1157endif;
1158""" % bcb_dev)
1159
1160 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001161 # For downgrade OTAs, we prefer to use the update-binary in the source
1162 # build that is actually newer than the one in the target build.
1163 if OPTIONS.downgrade:
1164 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1165 else:
1166 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001167 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001168 WriteMetadata(metadata, output_zip)
1169
Doug Zongker32b527d2014-03-04 10:03:02 -08001170
Tao Bao9bc6bb22015-11-09 16:58:28 -08001171def WriteVerifyPackage(input_zip, output_zip):
1172 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1173
1174 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1175 recovery_mount_options = OPTIONS.info_dict.get(
1176 "recovery_mount_options")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001177 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -07001178 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001179 oem_dicts = _LoadOemDicts(script, oem_props, recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001180
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001181 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
1182 OPTIONS.info_dict)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001183 metadata = {
1184 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001185 "pre-device": GetOemProperty("ro.product.device", oem_props,
1186 oem_dicts and oem_dicts[0],
Tao Bao9bc6bb22015-11-09 16:58:28 -08001187 OPTIONS.info_dict),
1188 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1189 }
1190
1191 device_specific = common.DeviceSpecificParams(
1192 input_zip=input_zip,
1193 input_version=OPTIONS.info_dict["recovery_api_version"],
1194 output_zip=output_zip,
1195 script=script,
1196 input_tmp=OPTIONS.input_tmp,
1197 metadata=metadata,
1198 info_dict=OPTIONS.info_dict)
1199
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001200 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001201
1202 script.Print("Verifying device images against %s..." % target_fp)
1203 script.AppendExtra("")
1204
1205 script.Print("Verifying boot...")
1206 boot_img = common.GetBootableImage(
1207 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1208 boot_type, boot_device = common.GetTypeAndDevice(
1209 "/boot", OPTIONS.info_dict)
1210 script.Verify("%s:%s:%d:%s" % (
1211 boot_type, boot_device, boot_img.size, boot_img.sha1))
1212 script.AppendExtra("")
1213
1214 script.Print("Verifying recovery...")
1215 recovery_img = common.GetBootableImage(
1216 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1217 recovery_type, recovery_device = common.GetTypeAndDevice(
1218 "/recovery", OPTIONS.info_dict)
1219 script.Verify("%s:%s:%d:%s" % (
1220 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1221 script.AppendExtra("")
1222
1223 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1224 system_tgt.ResetFileMap()
1225 system_diff = common.BlockDifference("system", system_tgt, src=None)
1226 system_diff.WriteStrictVerifyScript(script)
1227
1228 if HasVendorPartition(input_zip):
1229 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1230 vendor_tgt.ResetFileMap()
1231 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1232 vendor_diff.WriteStrictVerifyScript(script)
1233
1234 # Device specific partitions, such as radio, bootloader and etc.
1235 device_specific.VerifyOTA_Assertions()
1236
1237 script.SetProgress(1.0)
1238 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001239 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001240 WriteMetadata(metadata, output_zip)
1241
1242
Tao Baoc098e9e2016-01-07 13:03:56 -08001243def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1244 source_file=None):
1245 """Generate an Android OTA package that has A/B update payload."""
1246
Tao Bao2dd1c482017-02-03 16:49:39 -08001247 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1248 expected_length=None):
1249 """Compute the streaming metadata for a given zip.
1250
1251 When 'reserve_space' is True, we reserve extra space for the offset and
1252 length of the metadata entry itself, although we don't know the final
1253 values until the package gets signed. This function will be called again
1254 after signing. We then write the actual values and pad the string to the
1255 length we set earlier. Note that we can't use the actual length of the
1256 metadata entry in the second run. Otherwise the offsets for other entries
1257 will be changing again.
1258 """
Tao Baoc96316c2017-01-24 22:10:49 -08001259
1260 def ComputeEntryOffsetSize(name):
1261 """Compute the zip entry offset and size."""
1262 info = zip_file.getinfo(name)
1263 offset = info.header_offset + len(info.FileHeader())
1264 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001265 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001266
1267 # payload.bin and payload_properties.txt must exist.
1268 offsets = [ComputeEntryOffsetSize('payload.bin'),
1269 ComputeEntryOffsetSize('payload_properties.txt')]
1270
1271 # care_map.txt is available only if dm-verity is enabled.
1272 if 'care_map.txt' in zip_file.namelist():
1273 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001274
1275 # 'META-INF/com/android/metadata' is required. We don't know its actual
1276 # offset and length (as well as the values for other entries). So we
1277 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1278 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1279 # beginning of the zip), as well as the possible value changes in other
1280 # entries.
1281 if reserve_space:
1282 offsets.append('metadata:' + ' ' * 10)
1283 else:
1284 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1285
1286 value = ','.join(offsets)
1287 if expected_length is not None:
1288 assert len(value) <= expected_length, \
1289 'Insufficient reserved space: reserved=%d, actual=%d' % (
1290 expected_length, len(value))
1291 value += ' ' * (expected_length - len(value))
1292 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001293
Alex Deymod8d96ec2016-06-10 16:38:31 -07001294 # The place where the output from the subprocess should go.
1295 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1296
Tao Baoc098e9e2016-01-07 13:03:56 -08001297 # Setup signing keys.
1298 if OPTIONS.package_key is None:
1299 OPTIONS.package_key = OPTIONS.info_dict.get(
1300 "default_system_dev_certificate",
1301 "build/target/product/security/testkey")
1302
Tao Baodea0f8b2016-06-20 17:55:06 -07001303 # A/B updater expects a signing key in RSA format. Gets the key ready for
1304 # later use in step 3, unless a payload_signer has been specified.
1305 if OPTIONS.payload_signer is None:
1306 cmd = ["openssl", "pkcs8",
1307 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1308 "-inform", "DER", "-nocrypt"]
1309 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1310 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001311 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1312 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001313 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001314
Tao Baodea0f8b2016-06-20 17:55:06 -07001315 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001316 temp_zip_file = tempfile.NamedTemporaryFile()
1317 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1318 compression=zipfile.ZIP_DEFLATED)
1319
1320 # Metadata to comply with Android OTA package format.
1321 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001322 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001323 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001324 oem_dicts = _LoadOemDicts(script, None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001325
1326 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001327 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001328 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001329 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1330 OPTIONS.info_dict),
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001331 "pre-device": GetOemProperty("ro.product.device", oem_props,
1332 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001333 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001334 "ota-required-cache": "0",
1335 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001336 }
1337
1338 if source_file is not None:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001339 metadata["pre-build"] = CalculateFingerprint(oem_props,
1340 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001341 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001342 metadata["pre-build-incremental"] = GetBuildProp(
1343 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001344
Tao Baob31892e2017-02-07 11:21:17 -08001345 HandleDowngradeMetadata(metadata)
1346 else:
1347 metadata["post-timestamp"] = GetBuildProp(
1348 "ro.build.date.utc", OPTIONS.info_dict)
1349
Tao Baoc098e9e2016-01-07 13:03:56 -08001350 # 1. Generate payload.
1351 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1352 cmd = ["brillo_update_payload", "generate",
1353 "--payload", payload_file,
1354 "--target_image", target_file]
1355 if source_file is not None:
1356 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001357 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1358 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001359 assert p1.returncode == 0, "brillo_update_payload generate failed"
1360
1361 # 2. Generate hashes of the payload and metadata files.
1362 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1363 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1364 cmd = ["brillo_update_payload", "hash",
1365 "--unsigned_payload", payload_file,
1366 "--signature_size", "256",
1367 "--metadata_hash_file", metadata_sig_file,
1368 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001369 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1370 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001371 assert p1.returncode == 0, "brillo_update_payload hash failed"
1372
1373 # 3. Sign the hashes and insert them back into the payload file.
1374 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1375 suffix=".bin")
1376 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1377 suffix=".bin")
1378 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001379 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001380 cmd = [OPTIONS.payload_signer]
1381 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001382 else:
1383 cmd = ["openssl", "pkeyutl", "-sign",
1384 "-inkey", rsa_key,
1385 "-pkeyopt", "digest:sha256"]
1386 cmd.extend(["-in", payload_sig_file,
1387 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001388 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1389 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001390 assert p1.returncode == 0, "openssl sign payload failed"
1391
1392 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001393 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001394 cmd = [OPTIONS.payload_signer]
1395 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001396 else:
1397 cmd = ["openssl", "pkeyutl", "-sign",
1398 "-inkey", rsa_key,
1399 "-pkeyopt", "digest:sha256"]
1400 cmd.extend(["-in", metadata_sig_file,
1401 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001402 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1403 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001404 assert p1.returncode == 0, "openssl sign metadata failed"
1405
1406 # 3c. Insert the signatures back into the payload file.
1407 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1408 suffix=".bin")
1409 cmd = ["brillo_update_payload", "sign",
1410 "--unsigned_payload", payload_file,
1411 "--payload", signed_payload_file,
1412 "--signature_size", "256",
1413 "--metadata_signature_file", signed_metadata_sig_file,
1414 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001415 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1416 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001417 assert p1.returncode == 0, "brillo_update_payload sign failed"
1418
Alex Deymo19241c12016-02-04 22:29:29 -08001419 # 4. Dump the signed payload properties.
1420 properties_file = common.MakeTempFile(prefix="payload-properties-",
1421 suffix=".txt")
1422 cmd = ["brillo_update_payload", "properties",
1423 "--payload", signed_payload_file,
1424 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001425 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1426 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001427 assert p1.returncode == 0, "brillo_update_payload properties failed"
1428
Tao Bao7c5dc572016-06-14 17:48:11 -07001429 if OPTIONS.wipe_user_data:
1430 with open(properties_file, "a") as f:
1431 f.write("POWERWASH=1\n")
1432 metadata["ota-wipe"] = "yes"
1433
Tao Baoc96316c2017-01-24 22:10:49 -08001434 # Add the signed payload file and properties into the zip. In order to
1435 # support streaming, we pack payload.bin, payload_properties.txt and
1436 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1437 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001438 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1439 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001440 common.ZipWrite(output_zip, properties_file,
1441 arcname="payload_properties.txt",
1442 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001443
Tianjie Xucfa86222016-03-07 16:31:19 -08001444 # If dm-verity is supported for the device, copy contents of care_map
1445 # into A/B OTA package.
1446 if OPTIONS.info_dict.get("verity") == "true":
1447 target_zip = zipfile.ZipFile(target_file, "r")
1448 care_map_path = "META/care_map.txt"
1449 namelist = target_zip.namelist()
1450 if care_map_path in namelist:
1451 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001452 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1453 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001454 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001455 print("Warning: cannot find care map file in target_file package")
Tianjie Xucfa86222016-03-07 16:31:19 -08001456 common.ZipClose(target_zip)
1457
Tao Bao2dd1c482017-02-03 16:49:39 -08001458 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001459 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001460 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001461 WriteMetadata(metadata, output_zip)
1462 common.ZipClose(output_zip)
1463
Tao Bao2dd1c482017-02-03 16:49:39 -08001464 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1465 # zip entries, as well as padding the entry headers. We do a preliminary
1466 # signing (with an incomplete metadata entry) to allow that to happen. Then
1467 # compute the zip entry offsets, write back the final metadata and do the
1468 # final signing.
1469 prelim_signing = tempfile.NamedTemporaryFile()
1470 SignOutput(temp_zip_file.name, prelim_signing.name)
1471 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001472
Tao Bao2dd1c482017-02-03 16:49:39 -08001473 # Open the signed zip. Compute the final metadata that's needed for streaming.
1474 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1475 compression=zipfile.ZIP_DEFLATED)
1476 expected_length = len(metadata['ota-streaming-property-files'])
1477 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1478 prelim_zip, reserve_space=False, expected_length=expected_length)
1479
1480 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1481 final_signing = tempfile.NamedTemporaryFile()
1482 output_zip = zipfile.ZipFile(final_signing, "w",
1483 compression=zipfile.ZIP_DEFLATED)
1484 for item in prelim_zip.infolist():
1485 if item.filename == METADATA_NAME:
1486 continue
1487
1488 data = prelim_zip.read(item.filename)
1489 out_info = copy.copy(item)
1490 common.ZipWriteStr(output_zip, out_info, data)
1491
1492 # Now write the final metadata entry.
1493 WriteMetadata(metadata, output_zip)
1494 common.ZipClose(prelim_zip)
1495 common.ZipClose(output_zip)
1496
1497 # Re-sign the package after updating the metadata entry.
1498 SignOutput(final_signing.name, output_file)
1499 final_signing.close()
1500
1501 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001502 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001503 actual = metadata['ota-streaming-property-files'].strip()
1504 expected = ComputeStreamingMetadata(output_zip)
1505 assert actual == expected, \
1506 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001507 common.ZipClose(output_zip)
1508
Tao Baoc098e9e2016-01-07 13:03:56 -08001509
Dan Albert8b72aef2015-03-23 19:13:21 -07001510class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001511 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001512 self.deferred_patch_list = None
Tao Bao89fbb0f2017-01-10 10:47:58 -08001513 print("Loading target...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001514 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001515 print("Loading source...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001516 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1517
1518 self.verbatim_targets = verbatim_targets = []
1519 self.patch_list = patch_list = []
1520 diffs = []
1521 self.renames = renames = {}
1522 known_paths = set()
1523 largest_source_size = 0
1524
1525 matching_file_cache = {}
1526 for fn, sf in source_data.items():
1527 assert fn == sf.name
1528 matching_file_cache["path:" + fn] = sf
1529 if fn in target_data.keys():
1530 AddToKnownPaths(fn, known_paths)
1531 # Only allow eligibility for filename/sha matching
1532 # if there isn't a perfect path match.
1533 if target_data.get(sf.name) is None:
1534 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1535 matching_file_cache["sha:" + sf.sha1] = sf
1536
1537 for fn in sorted(target_data.keys()):
1538 tf = target_data[fn]
1539 assert fn == tf.name
1540 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1541 if sf is not None and sf.name != tf.name:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001542 print("File has moved from " + sf.name + " to " + tf.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001543 renames[sf.name] = tf
1544
1545 if sf is None or fn in OPTIONS.require_verbatim:
1546 # This file should be included verbatim
1547 if fn in OPTIONS.prohibit_verbatim:
1548 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Tao Bao89fbb0f2017-01-10 10:47:58 -08001549 print("send", fn, "verbatim")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001550 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001551 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001552 if fn in target_data.keys():
1553 AddToKnownPaths(fn, known_paths)
1554 elif tf.sha1 != sf.sha1:
1555 # File is different; consider sending as a patch
1556 diffs.append(common.Difference(tf, sf))
1557 else:
1558 # Target file data identical to source (may still be renamed)
1559 pass
1560
1561 common.ComputeDifferences(diffs)
1562
1563 for diff in diffs:
1564 tf, sf, d = diff.GetPatch()
1565 path = "/".join(tf.name.split("/")[:-1])
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001566 if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001567 path not in known_paths:
1568 # patch is almost as big as the file; don't bother patching
1569 # or a patch + rename cannot take place due to the target
1570 # directory not existing
1571 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001572 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001573 if sf.name in renames:
1574 del renames[sf.name]
1575 AddToKnownPaths(tf.name, known_paths)
1576 else:
1577 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1578 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1579 largest_source_size = max(largest_source_size, sf.size)
1580
1581 self.largest_source_size = largest_source_size
1582
1583 def EmitVerification(self, script):
1584 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001585 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001586 if tf.name != sf.name:
1587 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1588 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1589 so_far += sf.size
1590 return so_far
1591
Michael Runge63f01de2014-10-28 19:24:19 -07001592 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001593 for fn, _, sha1 in self.verbatim_targets:
1594 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001595 script.FileCheck("/"+fn, sha1)
1596 for tf, _, _, _ in self.patch_list:
1597 script.FileCheck(tf.name, tf.sha1)
1598
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001599 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001600 file_list = ["/" + i[0] for i in self.verbatim_targets]
1601 file_list += ["/" + i for i in self.source_data
1602 if i not in self.target_data and i not in self.renames]
1603 file_list += list(extras)
1604 # Sort the list in descending order, which removes all the files first
1605 # before attempting to remove the folder. (Bug: 22960996)
1606 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001607
1608 def TotalPatchSize(self):
1609 return sum(i[1].size for i in self.patch_list)
1610
1611 def EmitPatches(self, script, total_patch_size, so_far):
1612 self.deferred_patch_list = deferred_patch_list = []
1613 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001614 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001615 if tf.name == "system/build.prop":
1616 deferred_patch_list.append(item)
1617 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001618 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001619 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001620 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1621 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001622 so_far += tf.size
1623 script.SetProgress(so_far / total_patch_size)
1624 return so_far
1625
1626 def EmitDeferredPatches(self, script):
1627 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001628 tf, sf, _, _ = item
1629 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1630 "patch/" + sf.name + ".p")
1631 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001632
1633 def EmitRenames(self, script):
1634 if len(self.renames) > 0:
1635 script.Print("Renaming files...")
1636 for src, tgt in self.renames.iteritems():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001637 print("Renaming " + src + " to " + tgt.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001638 script.RenameFile(src, tgt.name)
1639
1640
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001641def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001642 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1643 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1644
Doug Zongker26e66192014-02-20 13:22:07 -08001645 if (OPTIONS.block_based and
1646 target_has_recovery_patch and
1647 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001648 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1649
Doug Zongker37974732010-09-16 17:44:38 -07001650 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1651 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001652
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001653 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001654 print("WARNING: generating edify script for a source that "
1655 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001656 script = edify_generator.EdifyGenerator(
1657 source_version, OPTIONS.target_info_dict,
1658 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001659
Tao Bao34b47bf2015-06-22 19:17:41 -07001660 recovery_mount_options = OPTIONS.source_info_dict.get(
1661 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001662 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1663 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001664 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -07001665 if source_oem_props or target_oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001666 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -07001667
Dan Albert8b72aef2015-03-23 19:13:21 -07001668 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001669 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001670 oem_dicts and oem_dicts[0],
1671 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001672 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001673 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001674
Tao Baob31892e2017-02-07 11:21:17 -08001675 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -08001676
Doug Zongker05d3dea2009-06-22 11:32:31 -07001677 device_specific = common.DeviceSpecificParams(
1678 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001679 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001680 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001681 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001682 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001683 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001684 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001685 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001686
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001687 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001688 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001689 if HasVendorPartition(target_zip):
1690 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001691 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001692 else:
1693 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001694
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001695 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001696 OPTIONS.target_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001697 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001698 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001699
Tao Bao3e30d972016-03-15 13:20:19 -07001700 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001701 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001702 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001703 script.AssertSomeThumbprint(
1704 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1705 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001706 elif source_oem_props is None and target_oem_props is not None:
1707 script.AssertFingerprintOrThumbprint(
1708 source_fp,
1709 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1710 else:
1711 script.AssertFingerprintOrThumbprint(
1712 target_fp,
1713 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001714
Doug Zongker2ea21062010-04-28 16:05:21 -07001715 metadata["pre-build"] = source_fp
1716 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001717 metadata["pre-build-incremental"] = GetBuildProp(
1718 "ro.build.version.incremental", OPTIONS.source_info_dict)
1719 metadata["post-build-incremental"] = GetBuildProp(
1720 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001721
Doug Zongker55d93282011-01-25 17:03:34 -08001722 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001723 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1724 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001725 target_boot = common.GetBootableImage(
1726 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001727 updating_boot = (not OPTIONS.two_step and
1728 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001729
Doug Zongker55d93282011-01-25 17:03:34 -08001730 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001731 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1732 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001733 target_recovery = common.GetBootableImage(
1734 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001735 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001736
Doug Zongker881dd402009-09-20 14:03:55 -07001737 # Here's how we divide up the progress bar:
1738 # 0.1 for verifying the start state (PatchCheck calls)
1739 # 0.8 for applying patches (ApplyPatch calls)
1740 # 0.1 for unpacking verbatim files, symlinking, and doing the
1741 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001742
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001743 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001744 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001745
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001746 # Two-step incremental package strategy (in chronological order,
1747 # which is *not* the order in which the generated script has
1748 # things):
1749 #
1750 # if stage is not "2/3" or "3/3":
1751 # do verification on current system
1752 # write recovery image to boot partition
1753 # set stage to "2/3"
1754 # reboot to boot partition and restart recovery
1755 # else if stage is "2/3":
1756 # write recovery image to recovery partition
1757 # set stage to "3/3"
1758 # reboot to recovery partition and restart recovery
1759 # else:
1760 # (stage must be "3/3")
1761 # perform update:
1762 # patch system files, etc.
1763 # force full install of new boot image
1764 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001765 # complete script normally
1766 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001767
1768 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001769 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001770 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001771 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001772 assert fs.fs_type.upper() == "EMMC", \
1773 "two-step packages only supported on devices with EMMC /misc partitions"
1774 bcb_dev = {"bcb_dev": fs.device}
1775 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1776 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001777if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001778""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001779
1780 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1781 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001782 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001783 script.WriteRawImage("/recovery", "recovery.img")
1784 script.AppendExtra("""
1785set_stage("%(bcb_dev)s", "3/3");
1786reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001787else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001788""" % bcb_dev)
1789
Tao Baod42e97e2016-11-30 12:11:57 -08001790 # Stage 1/3: (a) Verify the current system.
1791 script.Comment("Stage 1/3")
1792
Tao Bao6c55a8a2015-04-08 15:30:27 -07001793 # Dump fingerprints
1794 script.Print("Source: %s" % (source_fp,))
1795 script.Print("Target: %s" % (target_fp,))
1796
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001797 script.Print("Verifying current system...")
1798
Doug Zongkere5ff5902012-01-17 10:55:37 -08001799 device_specific.IncrementalOTA_VerifyBegin()
1800
Doug Zongker881dd402009-09-20 14:03:55 -07001801 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001802 so_far = system_diff.EmitVerification(script)
1803 if vendor_diff:
1804 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001805
Tao Baod8d14be2016-02-04 14:26:02 -08001806 size = []
1807 if system_diff.patch_list:
1808 size.append(system_diff.largest_source_size)
1809 if vendor_diff:
1810 if vendor_diff.patch_list:
1811 size.append(vendor_diff.largest_source_size)
1812
Doug Zongker5da317e2009-06-02 13:38:17 -07001813 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001814 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001815 _, _, d = d.ComputePatch()
Tao Bao89fbb0f2017-01-10 10:47:58 -08001816 print("boot target: %d source: %d diff: %d" % (
1817 target_boot.size, source_boot.size, len(d)))
Doug Zongker5da317e2009-06-02 13:38:17 -07001818
Doug Zongker048e7ca2009-06-15 14:31:53 -07001819 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001820
Tao Baodd24da92015-07-29 14:09:23 -07001821 boot_type, boot_device = common.GetTypeAndDevice(
1822 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001823
1824 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1825 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001826 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001827 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001828 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001829 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001830
Tao Baod8d14be2016-02-04 14:26:02 -08001831 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001832 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001833
Doug Zongker05d3dea2009-06-22 11:32:31 -07001834 device_specific.IncrementalOTA_VerifyEnd()
1835
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001836 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001837 # Stage 1/3: (b) Write recovery image to /boot.
1838 _WriteRecoveryImageToBoot(script, output_zip)
1839
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001840 script.AppendExtra("""
1841set_stage("%(bcb_dev)s", "2/3");
1842reboot_now("%(bcb_dev)s", "");
1843else
1844""" % bcb_dev)
1845
Tao Baod42e97e2016-11-30 12:11:57 -08001846 # Stage 3/3: Make changes.
1847 script.Comment("Stage 3/3")
1848
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001849 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001850
Doug Zongkere5ff5902012-01-17 10:55:37 -08001851 device_specific.IncrementalOTA_InstallBegin()
1852
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001853 if OPTIONS.two_step:
1854 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1855 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001856 print("writing full boot image (forced by two-step mode)")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001857
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001858 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001859 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1860 if vendor_diff:
1861 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001862
Doug Zongker881dd402009-09-20 14:03:55 -07001863 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001864 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1865 if vendor_diff:
1866 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001867 if updating_boot:
1868 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001869
1870 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001871 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1872 if vendor_diff:
1873 script.Print("Patching vendor files...")
1874 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001875
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001876 if not OPTIONS.two_step:
1877 if updating_boot:
1878 # Produce the boot image by applying a patch to the current
1879 # contents of the boot partition, and write it back to the
1880 # partition.
1881 script.Print("Patching boot image...")
1882 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1883 % (boot_type, boot_device,
1884 source_boot.size, source_boot.sha1,
1885 target_boot.size, target_boot.sha1),
1886 "-",
1887 target_boot.size, target_boot.sha1,
1888 source_boot.sha1, "patch/boot.img.p")
1889 so_far += target_boot.size
1890 script.SetProgress(so_far / total_patch_size)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001891 print("boot image changed; including.")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001892 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001893 print("boot image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001894
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001895 system_items = ItemSet("system", "META/filesystem_config.txt")
1896 if vendor_diff:
1897 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1898
Doug Zongkereef39442009-04-02 12:14:19 -07001899 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001900 # Recovery is generated as a patch using both the boot image
1901 # (which contains the same linux kernel as recovery) and the file
1902 # /system/etc/recovery-resource.dat (which contains all the images
1903 # used in the recovery UI) as sources. This lets us minimize the
1904 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001905 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001906 # For older builds where recovery-resource.dat is not present, we
1907 # use only the boot image as the source.
1908
Doug Zongkerc9253822014-02-04 12:17:58 -08001909 if not target_has_recovery_patch:
1910 def output_sink(fn, data):
1911 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001912 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001913
1914 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1915 target_recovery, target_boot)
1916 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001917 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001918 "/system/etc/install-recovery.sh"])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001919 print("recovery image changed; including as patch from boot.")
Doug Zongkereef39442009-04-02 12:14:19 -07001920 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001921 print("recovery image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001922
Doug Zongker881dd402009-09-20 14:03:55 -07001923 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001924
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001925 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1926 if vendor_diff:
1927 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1928
1929 temp_script = script.MakeTemporary()
1930 system_items.GetMetadata(target_zip)
1931 system_items.Get("system").SetPermissions(temp_script)
1932 if vendor_diff:
1933 vendor_items.GetMetadata(target_zip)
1934 vendor_items.Get("vendor").SetPermissions(temp_script)
1935
1936 # Note that this call will mess up the trees of Items, so make sure
1937 # we're done with them.
1938 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1939 if vendor_diff:
1940 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001941
1942 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001943 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1944
1945 # Delete all the symlinks in source that aren't in target. This
1946 # needs to happen before verbatim files are unpacked, in case a
1947 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001948
1949 # If a symlink in the source will be replaced by a regular file, we cannot
1950 # delete the symlink/file in case the package gets applied again. For such
1951 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1952 # (Bug: 23646151)
1953 replaced_symlinks = dict()
1954 if system_diff:
1955 for i in system_diff.verbatim_targets:
1956 replaced_symlinks["/%s" % (i[0],)] = i[2]
1957 if vendor_diff:
1958 for i in vendor_diff.verbatim_targets:
1959 replaced_symlinks["/%s" % (i[0],)] = i[2]
1960
1961 if system_diff:
1962 for tf in system_diff.renames.values():
1963 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1964 if vendor_diff:
1965 for tf in vendor_diff.renames.values():
1966 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1967
1968 always_delete = []
1969 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001970 for dest, link in source_symlinks:
1971 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001972 if link in replaced_symlinks:
1973 may_delete.append((link, replaced_symlinks[link]))
1974 else:
1975 always_delete.append(link)
1976 script.DeleteFiles(always_delete)
1977 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001978
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001979 if system_diff.verbatim_targets:
1980 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001981 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001982 if vendor_diff and vendor_diff.verbatim_targets:
1983 script.Print("Unpacking new vendor files...")
1984 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001985
Doug Zongkerc9253822014-02-04 12:17:58 -08001986 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001987 script.Print("Unpacking new recovery...")
1988 script.UnpackPackageDir("recovery", "/system")
1989
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001990 system_diff.EmitRenames(script)
1991 if vendor_diff:
1992 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001993
Doug Zongker05d3dea2009-06-22 11:32:31 -07001994 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001995
1996 # Create all the symlinks that don't already exist, or point to
1997 # somewhere different than what we want. Delete each symlink before
1998 # creating it, since the 'symlink' command won't overwrite.
1999 to_create = []
2000 for dest, link in target_symlinks:
2001 if link in source_symlinks_d:
2002 if dest != source_symlinks_d[link]:
2003 to_create.append((dest, link))
2004 else:
2005 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002006 script.DeleteFiles([i[1] for i in to_create])
2007 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07002008
2009 # Now that the symlinks are created, we can set all the
2010 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002011 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07002012
Doug Zongker881dd402009-09-20 14:03:55 -07002013 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07002014 device_specific.IncrementalOTA_InstallEnd()
2015
Doug Zongker1c390a22009-05-14 19:06:36 -07002016 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07002017 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07002018
Doug Zongkere92f15a2011-08-26 13:46:40 -07002019 # Patch the build.prop file last, so if something fails but the
2020 # device can still come up, it appears to be the old build and will
2021 # get set the OTA package again to retry.
2022 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07002023 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07002024
Doug Zongker922206e2014-03-04 13:16:24 -08002025 if OPTIONS.wipe_user_data:
2026 script.Print("Erasing user data...")
2027 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08002028 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08002029
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002030 if OPTIONS.two_step:
2031 script.AppendExtra("""
2032set_stage("%(bcb_dev)s", "");
2033endif;
2034endif;
2035""" % bcb_dev)
2036
Michael Runge63f01de2014-10-28 19:24:19 -07002037 if OPTIONS.verify and system_diff:
2038 script.Print("Remounting and verifying system partition files...")
2039 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08002040 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002041 system_diff.EmitExplicitTargetVerification(script)
2042
2043 if OPTIONS.verify and vendor_diff:
2044 script.Print("Remounting and verifying vendor partition files...")
2045 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08002046 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002047 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08002048
2049 # For downgrade OTAs, we prefer to use the update-binary in the source
2050 # build that is actually newer than the one in the target build.
2051 if OPTIONS.downgrade:
2052 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
2053 else:
2054 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07002055
Tao Baod8d14be2016-02-04 14:26:02 -08002056 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07002057 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07002058
2059
2060def main(argv):
2061
2062 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08002063 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002064 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07002065 elif o in ("-k", "--package_key"):
2066 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002067 elif o in ("-i", "--incremental_from"):
2068 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002069 elif o == "--full_radio":
2070 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002071 elif o == "--full_bootloader":
2072 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002073 elif o in ("-w", "--wipe_user_data"):
2074 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002075 elif o == "--downgrade":
2076 OPTIONS.downgrade = True
2077 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07002078 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002079 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002080 elif o == "--oem_no_mount":
2081 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002082 elif o in ("-e", "--extra_script"):
2083 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002084 elif o in ("-t", "--worker_threads"):
2085 if a.isdigit():
2086 OPTIONS.worker_threads = int(a)
2087 else:
2088 raise ValueError("Cannot parse value %r for option %r - only "
2089 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002090 elif o in ("-2", "--two_step"):
2091 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002092 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002093 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002094 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002095 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002096 elif o == "--block":
2097 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002098 elif o in ("-b", "--binary"):
2099 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002100 elif o in ("--no_fallback_to_full",):
2101 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002102 elif o == "--stash_threshold":
2103 try:
2104 OPTIONS.stash_threshold = float(a)
2105 except ValueError:
2106 raise ValueError("Cannot parse value %r for option %r - expecting "
2107 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002108 elif o == "--gen_verify":
2109 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002110 elif o == "--log_diff":
2111 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002112 elif o == "--payload_signer":
2113 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002114 elif o == "--payload_signer_args":
2115 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002116 else:
2117 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002118 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002119
2120 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08002121 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002122 extra_long_opts=[
2123 "board_config=",
2124 "package_key=",
2125 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002126 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002127 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002128 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002129 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07002130 "extra_script=",
2131 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002132 "two_step",
2133 "no_signing",
2134 "block",
2135 "binary=",
2136 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002137 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002138 "verify",
2139 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002140 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002141 "gen_verify",
2142 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002143 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002144 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002145 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002146
2147 if len(args) != 2:
2148 common.Usage(__doc__)
2149 sys.exit(1)
2150
Tao Bao5d182562016-02-23 11:38:39 -08002151 if OPTIONS.downgrade:
2152 # Sanity check to enforce a data wipe.
2153 if not OPTIONS.wipe_user_data:
2154 raise ValueError("Cannot downgrade without a data wipe")
2155
2156 # We should only allow downgrading incrementals (as opposed to full).
2157 # Otherwise the device may go back from arbitrary build with this full
2158 # OTA package.
2159 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002160 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002161
Tao Baoc098e9e2016-01-07 13:03:56 -08002162 # Load the dict file from the zip directly to have a peek at the OTA type.
2163 # For packages using A/B update, unzipping is not needed.
2164 input_zip = zipfile.ZipFile(args[0], "r")
2165 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2166 common.ZipClose(input_zip)
2167
2168 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2169
2170 if ab_update:
2171 if OPTIONS.incremental_source is not None:
2172 OPTIONS.target_info_dict = OPTIONS.info_dict
2173 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2174 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2175 common.ZipClose(source_zip)
2176
2177 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002178 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002179 common.DumpInfoDict(OPTIONS.info_dict)
2180
2181 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002182 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002183 common.DumpInfoDict(OPTIONS.source_info_dict)
2184
2185 WriteABOTAPackageWithBrilloScript(
2186 target_file=args[0],
2187 output_file=args[1],
2188 source_file=OPTIONS.incremental_source)
2189
Tao Bao89fbb0f2017-01-10 10:47:58 -08002190 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002191 return
2192
Doug Zongker1c390a22009-05-14 19:06:36 -07002193 if OPTIONS.extra_script is not None:
2194 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2195
Tao Bao89fbb0f2017-01-10 10:47:58 -08002196 print("unzipping target target-files...")
Doug Zongker55d93282011-01-25 17:03:34 -08002197 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002198
Doug Zongkereef39442009-04-02 12:14:19 -07002199 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002200 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002201
Doug Zongker37974732010-09-16 17:44:38 -07002202 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002203 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07002204 common.DumpInfoDict(OPTIONS.info_dict)
2205
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002206 # If the caller explicitly specified the device-specific extensions
2207 # path via -s/--device_specific, use that. Otherwise, use
2208 # META/releasetools.py if it is present in the target target_files.
2209 # Otherwise, take the path of the file from 'tool_extensions' in the
2210 # info dict and look for that in the local filesystem, relative to
2211 # the current directory.
2212
Doug Zongker37974732010-09-16 17:44:38 -07002213 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002214 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2215 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08002216 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002217 OPTIONS.device_specific = from_input
2218 else:
2219 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2220
Doug Zongker37974732010-09-16 17:44:38 -07002221 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002222 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002223
Tao Baoc098e9e2016-01-07 13:03:56 -08002224 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002225 raise common.ExternalError(
2226 "--- target build has specified no recovery ---")
2227
Tao Bao767e3ac2015-11-10 12:19:19 -08002228 # Use the default key to sign the package if not specified with package_key.
2229 if not OPTIONS.no_signing:
2230 if OPTIONS.package_key is None:
2231 OPTIONS.package_key = OPTIONS.info_dict.get(
2232 "default_system_dev_certificate",
2233 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002234
Tao Bao767e3ac2015-11-10 12:19:19 -08002235 # Set up the output zip. Create a temporary zip file if signing is needed.
2236 if OPTIONS.no_signing:
2237 if os.path.exists(args[1]):
2238 os.unlink(args[1])
2239 output_zip = zipfile.ZipFile(args[1], "w",
2240 compression=zipfile.ZIP_DEFLATED)
2241 else:
2242 temp_zip_file = tempfile.NamedTemporaryFile()
2243 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2244 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002245
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002246 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002247 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002248 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002249 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002250 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002251
Tao Bao9bc6bb22015-11-09 16:58:28 -08002252 # Generate a verify package.
2253 if OPTIONS.gen_verify:
2254 WriteVerifyPackage(input_zip, output_zip)
2255
Tao Bao767e3ac2015-11-10 12:19:19 -08002256 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002257 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002258 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002259
2260 # Generate an incremental OTA. It will fall back to generate a full OTA on
2261 # failure unless no_fallback_to_full is specified.
2262 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002263 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08002264 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2265 OPTIONS.incremental_source)
2266 OPTIONS.target_info_dict = OPTIONS.info_dict
2267 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2268 OPTIONS.source_tmp)
2269 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002270 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002271 common.DumpInfoDict(OPTIONS.source_info_dict)
2272 try:
2273 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002274 if OPTIONS.log_diff:
2275 out_file = open(OPTIONS.log_diff, 'w')
2276 import target_files_diff
2277 target_files_diff.recursiveDiff('',
2278 OPTIONS.source_tmp,
2279 OPTIONS.input_tmp,
2280 out_file)
2281 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002282 except ValueError:
2283 if not OPTIONS.fallback_to_full:
2284 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08002285 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002286 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002287 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002288
Tao Bao767e3ac2015-11-10 12:19:19 -08002289 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002290
Tao Bao767e3ac2015-11-10 12:19:19 -08002291 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002292 if not OPTIONS.no_signing:
2293 SignOutput(temp_zip_file.name, args[1])
2294 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002295
Tao Bao89fbb0f2017-01-10 10:47:58 -08002296 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002297
2298
2299if __name__ == '__main__':
2300 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002301 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002302 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002303 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002304 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002305 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002306 finally:
2307 common.Cleanup()