blob: 0a7653c5cad9dec0e122b9f130fb93ffc4a4ce37 [file] [log] [blame]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001# Copyright (C) 2009 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Doug Zongkerc494d7c2009-06-18 08:43:44 -070015import re
16
17import common
18
Kelvin Zhang782b7522023-10-31 19:33:55 -070019# map recovery.fstab's fs_types to mount/format "partition types"
20PARTITION_TYPES = {
21 "ext4": "EMMC",
22 "emmc": "EMMC",
23 "f2fs": "EMMC",
24 "squashfs": "EMMC",
25 "erofs": "EMMC"
26}
27
Kelvin Zhang513b86e2023-10-27 13:27:07 -070028
29class ErrorCode(object):
30 """Define error_codes for failures that happen during the actual
31 update package installation.
32
33 Error codes 0-999 are reserved for failures before the package
34 installation (i.e. low battery, package verification failure).
35 Detailed code in 'bootable/recovery/error_code.h' """
36
37 SYSTEM_VERIFICATION_FAILURE = 1000
38 SYSTEM_UPDATE_FAILURE = 1001
39 SYSTEM_UNEXPECTED_CONTENTS = 1002
40 SYSTEM_NONZERO_CONTENTS = 1003
41 SYSTEM_RECOVER_FAILURE = 1004
42 VENDOR_VERIFICATION_FAILURE = 2000
43 VENDOR_UPDATE_FAILURE = 2001
44 VENDOR_UNEXPECTED_CONTENTS = 2002
45 VENDOR_NONZERO_CONTENTS = 2003
46 VENDOR_RECOVER_FAILURE = 2004
47 OEM_PROP_MISMATCH = 3000
48 FINGERPRINT_MISMATCH = 3001
49 THUMBPRINT_MISMATCH = 3002
50 OLDER_BUILD = 3003
51 DEVICE_MISMATCH = 3004
52 BAD_PATCH_FILE = 3005
53 INSUFFICIENT_CACHE_SPACE = 3006
54 TUNE_PARTITION_FAILURE = 3007
55 APPLY_PATCH_FAILURE = 3008
56
57
Doug Zongkerc494d7c2009-06-18 08:43:44 -070058class EdifyGenerator(object):
59 """Class to generate scripts in the 'edify' recovery script language
60 used from donut onwards."""
61
Tao Bao34b47bf2015-06-22 19:17:41 -070062 def __init__(self, version, info, fstab=None):
Doug Zongkerc494d7c2009-06-18 08:43:44 -070063 self.script = []
64 self.mounts = set()
Tao Baod8d14be2016-02-04 14:26:02 -080065 self._required_cache = 0
Doug Zongkerc494d7c2009-06-18 08:43:44 -070066 self.version = version
Doug Zongkerb4c7d322010-07-01 15:30:11 -070067 self.info = info
Tao Bao34b47bf2015-06-22 19:17:41 -070068 if fstab is None:
69 self.fstab = self.info.get("fstab", None)
70 else:
71 self.fstab = fstab
Doug Zongkerc494d7c2009-06-18 08:43:44 -070072
Tao Baod8d14be2016-02-04 14:26:02 -080073 @property
74 def required_cache(self):
75 """Return the minimum cache size to apply the update."""
76 return self._required_cache
77
Doug Zongkerc494d7c2009-06-18 08:43:44 -070078 @staticmethod
Dan Albert8b72aef2015-03-23 19:13:21 -070079 def WordWrap(cmd, linelen=80):
Doug Zongkerc494d7c2009-06-18 08:43:44 -070080 """'cmd' should be a function call with null characters after each
81 parameter (eg, "somefun(foo,\0bar,\0baz)"). This function wraps cmd
82 to a given line length, replacing nulls with spaces and/or newlines
83 to format it nicely."""
84 indent = cmd.index("(")+1
85 out = []
86 first = True
87 x = re.compile("^(.{,%d})\0" % (linelen-indent,))
88 while True:
89 if not first:
90 out.append(" " * indent)
91 first = False
92 m = x.search(cmd)
93 if not m:
94 parts = cmd.split("\0", 1)
95 out.append(parts[0]+"\n")
96 if len(parts) == 1:
97 break
98 else:
99 cmd = parts[1]
100 continue
101 out.append(m.group(1)+"\n")
102 cmd = cmd[m.end():]
103
104 return "".join(out).replace("\0", " ").rstrip("\n")
105
106 def AppendScript(self, other):
107 """Append the contents of another script (which should be created
108 with temporary=True) to this one."""
109 self.script.extend(other.script)
110
Tao Bao481bab82017-12-21 11:23:09 -0800111 def AssertOemProperty(self, name, values, oem_no_mount):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800112 """Assert that a property on the OEM paritition matches allowed values."""
Michael Runge6e836112014-04-15 17:40:21 -0700113 if not name:
114 raise ValueError("must specify an OEM property")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800115 if not values:
Michael Runge6e836112014-04-15 17:40:21 -0700116 raise ValueError("must specify the OEM value")
Tao Bao481bab82017-12-21 11:23:09 -0800117
118 if oem_no_mount:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800119 get_prop_command = 'getprop("%s")' % name
Tao Bao8608cde2016-02-25 19:49:55 -0800120 else:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800121 get_prop_command = 'file_getprop("/oem/oem.prop", "%s")' % name
122
123 cmd = ''
124 for value in values:
125 cmd += '%s == "%s" || ' % (get_prop_command, value)
126 cmd += (
127 'abort("E{code}: This package expects the value \\"{values}\\" for '
128 '\\"{name}\\"; this has value \\"" + '
129 '{get_prop_command} + "\\".");').format(
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700130 code=ErrorCode.OEM_PROP_MISMATCH,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800131 get_prop_command=get_prop_command, name=name,
132 values='\\" or \\"'.join(values))
Michael Runge6e836112014-04-15 17:40:21 -0700133 self.script.append(cmd)
134
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700135 def AssertSomeFingerprint(self, *fp):
Doug Zongkeraf845252014-05-09 08:29:05 -0700136 """Assert that the current recovery build fingerprint is one of *fp."""
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700137 if not fp:
138 raise ValueError("must specify some fingerprints")
Dan Albert8b72aef2015-03-23 19:13:21 -0700139 cmd = (' ||\n '.join([('getprop("ro.build.fingerprint") == "%s"') % i
140 for i in fp]) +
Tianjie Xu209db462016-05-24 17:34:52 -0700141 ' ||\n abort("E%d: Package expects build fingerprint of %s; '
142 'this device has " + getprop("ro.build.fingerprint") + ".");') % (
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700143 ErrorCode.FINGERPRINT_MISMATCH, " or ".join(fp))
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700144 self.script.append(cmd)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700145
Michael Runge6e836112014-04-15 17:40:21 -0700146 def AssertSomeThumbprint(self, *fp):
Doug Zongkeraf845252014-05-09 08:29:05 -0700147 """Assert that the current recovery build thumbprint is one of *fp."""
Geremy Condra36bd3652014-02-06 19:45:10 -0800148 if not fp:
Michael Runge6e836112014-04-15 17:40:21 -0700149 raise ValueError("must specify some thumbprints")
Dan Albert8b72aef2015-03-23 19:13:21 -0700150 cmd = (' ||\n '.join([('getprop("ro.build.thumbprint") == "%s"') % i
151 for i in fp]) +
Tianjie Xu209db462016-05-24 17:34:52 -0700152 ' ||\n abort("E%d: Package expects build thumbprint of %s; this '
Dan Albert8b72aef2015-03-23 19:13:21 -0700153 'device has " + getprop("ro.build.thumbprint") + ".");') % (
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700154 ErrorCode.THUMBPRINT_MISMATCH, " or ".join(fp))
Geremy Condra36bd3652014-02-06 19:45:10 -0800155 self.script.append(cmd)
156
Tao Bao3e30d972016-03-15 13:20:19 -0700157 def AssertFingerprintOrThumbprint(self, fp, tp):
158 """Assert that the current recovery build fingerprint is fp, or thumbprint
159 is tp."""
160 cmd = ('getprop("ro.build.fingerprint") == "{fp}" ||\n'
161 ' getprop("ro.build.thumbprint") == "{tp}" ||\n'
162 ' abort("Package expects build fingerprint of {fp} or '
163 'thumbprint of {tp}; this device has a fingerprint of " '
Tao Baod2d01e52017-06-05 12:25:46 -0700164 '+ getprop("ro.build.fingerprint") + " and a thumbprint of " '
Tao Bao3e30d972016-03-15 13:20:19 -0700165 '+ getprop("ro.build.thumbprint") + ".");').format(fp=fp, tp=tp)
166 self.script.append(cmd)
167
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700168 def AssertOlderBuild(self, timestamp, timestamp_text):
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700169 """Assert that the build on the device is older (or the same as)
170 the given timestamp."""
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700171 self.script.append(
172 ('(!less_than_int(%s, getprop("ro.build.date.utc"))) || '
Tianjie Xu209db462016-05-24 17:34:52 -0700173 'abort("E%d: Can\'t install this package (%s) over newer '
Tao Bao76def242017-11-21 09:25:31 -0800174 'build (" + getprop("ro.build.date") + ").");') % (
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700175 timestamp, ErrorCode.OLDER_BUILD, timestamp_text))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700176
177 def AssertDevice(self, device):
178 """Assert that the device identifier is the given string."""
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700179 cmd = ('getprop("ro.product.device") == "%s" || '
Tianjie Xu209db462016-05-24 17:34:52 -0700180 'abort("E%d: This package is for \\"%s\\" devices; '
Dan Albert8b72aef2015-03-23 19:13:21 -0700181 'this is a \\"" + getprop("ro.product.device") + "\\".");') % (
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700182 device, ErrorCode.DEVICE_MISMATCH, device)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700183 self.script.append(cmd)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700184
185 def AssertSomeBootloader(self, *bootloaders):
186 """Asert that the bootloader version is one of *bootloaders."""
187 cmd = ("assert(" +
188 " ||\0".join(['getprop("ro.bootloader") == "%s"' % (b,)
189 for b in bootloaders]) +
190 ");")
Dan Albert8b72aef2015-03-23 19:13:21 -0700191 self.script.append(self.WordWrap(cmd))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700192
193 def ShowProgress(self, frac, dur):
194 """Update the progress bar, advancing it over 'frac' over the next
Doug Zongker881dd402009-09-20 14:03:55 -0700195 'dur' seconds. 'dur' may be zero to advance it via SetProgress
196 commands instead of by time."""
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700197 self.script.append("show_progress(%f, %d);" % (frac, int(dur)))
198
Doug Zongker881dd402009-09-20 14:03:55 -0700199 def SetProgress(self, frac):
200 """Set the position of the progress bar within the chunk defined
201 by the most recent ShowProgress call. 'frac' should be in
202 [0,1]."""
203 self.script.append("set_progress(%f);" % (frac,))
204
Tao Bao51216552018-08-26 11:53:15 -0700205 def PatchCheck(self, filename, *sha1): # pylint: disable=unused-argument
206 """Checks that the given partition has the desired checksum.
207
208 The call to this function is being deprecated in favor of
209 PatchPartitionCheck(). It will try to parse and handle the old format,
210 unless the format is unknown.
211 """
212 tokens = filename.split(':')
213 assert len(tokens) == 6 and tokens[0] == 'EMMC', \
214 "Failed to handle unknown format. Use PatchPartitionCheck() instead."
215 source = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[2], tokens[3])
216 target = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[4], tokens[5])
217 self.PatchPartitionCheck(target, source)
218
219 def PatchPartitionCheck(self, target, source):
220 """Checks whether updater can patch the given partitions.
221
222 It checks the checksums of the given partitions. If none of them matches the
223 expected checksum, updater will additionally look for a backup on /cache.
224 """
Yifan Hongbdb32012020-05-07 12:38:53 -0700225 self._CheckSecondTokenNotSlotSuffixed(target, "PatchPartitionExprCheck")
226 self._CheckSecondTokenNotSlotSuffixed(source, "PatchPartitionExprCheck")
227 self.PatchPartitionExprCheck('"%s"' % target, '"%s"' % source)
228
229 def PatchPartitionExprCheck(self, target_expr, source_expr):
230 """Checks whether updater can patch the given partitions.
231
232 It checks the checksums of the given partitions. If none of them matches the
233 expected checksum, updater will additionally look for a backup on /cache.
234
235 Args:
236 target_expr: an Edify expression that serves as the target arg to
237 patch_partition. Must be evaluated to a string in the form of
238 foo:bar:baz:quux
239 source_expr: an Edify expression that serves as the source arg to
240 patch_partition. Must be evaluated to a string in the form of
241 foo:bar:baz:quux
242 """
Tao Bao51216552018-08-26 11:53:15 -0700243 self.script.append(self.WordWrap((
Yifan Hongbdb32012020-05-07 12:38:53 -0700244 'patch_partition_check({target},\0{source}) ||\n abort('
245 'concat("E{code}: \\"",{target},"\\" or \\"",{source},"\\" has '
246 'unexpected contents."));').format(
247 target=target_expr,
248 source=source_expr,
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700249 code=ErrorCode.BAD_PATCH_FILE)))
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800250
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700251 def CacheFreeSpaceCheck(self, amount):
252 """Check that there's at least 'amount' space that can be made
253 available on /cache."""
Tao Baod8d14be2016-02-04 14:26:02 -0800254 self._required_cache = max(self._required_cache, amount)
Tianjie Xu209db462016-05-24 17:34:52 -0700255 self.script.append(('apply_patch_space(%d) || abort("E%d: Not enough free '
256 'space on /cache to apply patches.");') % (
257 amount,
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700258 ErrorCode.INSUFFICIENT_CACHE_SPACE))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700259
Michael Runge7cd99ba2014-10-22 17:21:48 -0700260 def Mount(self, mount_point, mount_options_by_format=""):
261 """Mount the partition with the given mount_point.
262 mount_options_by_format:
263 [fs_type=option[,option]...[|fs_type=option[,option]...]...]
264 where option is optname[=optvalue]
265 E.g. ext4=barrier=1,nodelalloc,errors=panic|f2fs=errors=recover
266 """
Tao Bao34b47bf2015-06-22 19:17:41 -0700267 fstab = self.fstab
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700268 if fstab:
269 p = fstab[mount_point]
Michael Runge7cd99ba2014-10-22 17:21:48 -0700270 mount_dict = {}
271 if mount_options_by_format is not None:
272 for option in mount_options_by_format.split("|"):
273 if "=" in option:
274 key, value = option.split("=", 1)
275 mount_dict[key] = value
Tao Baodf06e962015-06-10 12:32:41 -0700276 mount_flags = mount_dict.get(p.fs_type, "")
277 if p.context is not None:
278 mount_flags = p.context + ("," + mount_flags if mount_flags else "")
Yifan Hongbdb32012020-05-07 12:38:53 -0700279 self.script.append('mount("%s", "%s", %s, "%s", "%s");' % (
Kelvin Zhang782b7522023-10-31 19:33:55 -0700280 p.fs_type, PARTITION_TYPES[p.fs_type],
Yifan Hongbdb32012020-05-07 12:38:53 -0700281 self._GetSlotSuffixDeviceForEntry(p),
Tao Baodf06e962015-06-10 12:32:41 -0700282 p.mount_point, mount_flags))
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700283 self.mounts.add(p.mount_point)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700284
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700285 def Comment(self, comment):
286 """Write a comment into the update script."""
287 self.script.append("")
288 for i in comment.split("\n"):
289 self.script.append("# " + i)
290 self.script.append("")
291
292 def Print(self, message):
293 """Log a message to the screen (if the logs are visible)."""
294 self.script.append('ui_print("%s");' % (message,))
295
Michael Runge3e286642014-11-21 00:46:03 -0800296 def TunePartition(self, partition, *options):
Tao Bao34b47bf2015-06-22 19:17:41 -0700297 fstab = self.fstab
Michael Runge3e286642014-11-21 00:46:03 -0800298 if fstab:
299 p = fstab[partition]
Dan Albert8b72aef2015-03-23 19:13:21 -0700300 if p.fs_type not in ("ext2", "ext3", "ext4"):
Michael Runge3e286642014-11-21 00:46:03 -0800301 raise ValueError("Partition %s cannot be tuned\n" % (partition,))
Dan Albert8b72aef2015-03-23 19:13:21 -0700302 self.script.append(
303 'tune2fs(' + "".join(['"%s", ' % (i,) for i in options]) +
Yifan Hongbdb32012020-05-07 12:38:53 -0700304 '%s) || abort("E%d: Failed to tune partition %s");' % (
305 self._GetSlotSuffixDeviceForEntry(p),
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700306 ErrorCode.TUNE_PARTITION_FAILURE, partition))
Michael Runge3e286642014-11-21 00:46:03 -0800307
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700308 def FormatPartition(self, partition):
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700309 """Format the given partition, specified by its mount point (eg,
310 "/system")."""
311
Tao Bao34b47bf2015-06-22 19:17:41 -0700312 fstab = self.fstab
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700313 if fstab:
314 p = fstab[partition]
Yifan Hongbdb32012020-05-07 12:38:53 -0700315 self.script.append('format("%s", "%s", %s, "%s", "%s");' %
Kelvin Zhang782b7522023-10-31 19:33:55 -0700316 (p.fs_type, PARTITION_TYPES[p.fs_type],
Yifan Hongbdb32012020-05-07 12:38:53 -0700317 self._GetSlotSuffixDeviceForEntry(p),
318 p.length, p.mount_point))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700319
Doug Zongker5fad2032014-02-24 08:13:45 -0800320 def WipeBlockDevice(self, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700321 if partition not in ("/system", "/vendor"):
322 raise ValueError(("WipeBlockDevice doesn't work on %s\n") % (partition,))
Tao Bao34b47bf2015-06-22 19:17:41 -0700323 fstab = self.fstab
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700324 size = self.info.get(partition.lstrip("/") + "_size", None)
Yifan Hongbdb32012020-05-07 12:38:53 -0700325 device = self._GetSlotSuffixDeviceForEntry(fstab[partition])
Doug Zongker5fad2032014-02-24 08:13:45 -0800326
Yifan Hongbdb32012020-05-07 12:38:53 -0700327 self.script.append('wipe_block_device(%s, %s);' % (device, size))
Doug Zongker5fad2032014-02-24 08:13:45 -0800328
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700329 def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs):
330 """Apply binary patches (in *patchpairs) to the given srcfile to
331 produce tgtfile (which may be "-" to indicate overwriting the
Tao Bao51216552018-08-26 11:53:15 -0700332 source file.
333
334 This edify function is being deprecated in favor of PatchPartition(). It
335 will try to redirect calls to PatchPartition() if possible. On unknown /
336 invalid inputs, raises an exception.
337 """
338 tokens = srcfile.split(':')
339 assert (len(tokens) == 6 and tokens[0] == 'EMMC' and tgtfile == '-' and
340 len(patchpairs) == 2), \
341 "Failed to handle unknown format. Use PatchPartition() instead."
342
Kelvin Zhangc693d952020-07-22 19:21:22 -0400343 # Also validity check the args.
Tao Bao51216552018-08-26 11:53:15 -0700344 assert tokens[3] == patchpairs[0], \
345 "Found mismatching values for source SHA-1: {} vs {}".format(
346 tokens[3], patchpairs[0])
347 assert int(tokens[4]) == tgtsize, \
348 "Found mismatching values for target size: {} vs {}".format(
349 tokens[4], tgtsize)
350 assert tokens[5] == tgtsha1, \
351 "Found mismatching values for target SHA-1: {} vs {}".format(
352 tokens[5], tgtsha1)
353
354 source = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[2], tokens[3])
355 target = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[4], tokens[5])
356 patch = patchpairs[1]
357 self.PatchPartition(target, source, patch)
358
359 def PatchPartition(self, target, source, patch):
Yifan Hongbdb32012020-05-07 12:38:53 -0700360 """
361 Applies the patch to the source partition and writes it to target.
362
363 Args:
364 target: the target arg to patch_partition. Must be in the form of
365 foo:bar:baz:quux
366 source: the source arg to patch_partition. Must be in the form of
367 foo:bar:baz:quux
368 patch: the patch arg to patch_partition. Must be an unquoted string.
369 """
370 self._CheckSecondTokenNotSlotSuffixed(target, "PatchPartitionExpr")
371 self._CheckSecondTokenNotSlotSuffixed(source, "PatchPartitionExpr")
372 self.PatchPartitionExpr('"%s"' % target, '"%s"' % source, '"%s"' % patch)
373
374 def PatchPartitionExpr(self, target_expr, source_expr, patch_expr):
375 """
376 Applies the patch to the source partition and writes it to target.
377
378 Args:
379 target_expr: an Edify expression that serves as the target arg to
380 patch_partition. Must be evaluated to a string in the form of
381 foo:bar:baz:quux
382 source_expr: an Edify expression that serves as the source arg to
383 patch_partition. Must be evaluated to a string in the form of
384 foo:bar:baz:quux
385 patch_expr: an Edify expression that serves as the patch arg to
386 patch_partition. Must be evaluated to a string.
387 """
Tao Bao51216552018-08-26 11:53:15 -0700388 self.script.append(self.WordWrap((
Yifan Hongbdb32012020-05-07 12:38:53 -0700389 'patch_partition({target},\0{source},\0'
390 'package_extract_file({patch})) ||\n'
391 ' abort(concat('
392 ' "E{code}: Failed to apply patch to ",{source}));').format(
393 target=target_expr,
394 source=source_expr,
395 patch=patch_expr,
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700396 code=ErrorCode.APPLY_PATCH_FAILURE)))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700397
Yifan Hongbdb32012020-05-07 12:38:53 -0700398 def _GetSlotSuffixDeviceForEntry(self, entry=None):
399 """
400 Args:
401 entry: the fstab entry of device "foo"
402 Returns:
403 An edify expression. Caller must not quote result.
404 If foo is slot suffixed, it returns
405 'add_slot_suffix("foo")'
406 Otherwise it returns
407 '"foo"' (quoted)
408 """
409 assert entry is not None
410 if entry.slotselect:
411 return 'add_slot_suffix("%s")' % entry.device
412 return '"%s"' % entry.device
413
414 def _CheckSecondTokenNotSlotSuffixed(self, s, fn):
415 lst = s.split(':')
P.Adarsh Reddy1ed2d662020-07-01 00:30:59 +0530416 assert(len(lst) == 4), "{} does not contain 4 tokens".format(s)
Yifan Hongbdb32012020-05-07 12:38:53 -0700417 if self.fstab:
P.Adarsh Reddy1ed2d662020-07-01 00:30:59 +0530418 entry = common.GetEntryForDevice(self.fstab, lst[1])
Yifan Hongbdb32012020-05-07 12:38:53 -0700419 if entry is not None:
420 assert not entry.slotselect, \
P.Adarsh Reddy1ed2d662020-07-01 00:30:59 +0530421 "Use %s because %s is slot suffixed" % (fn, lst[1])
Yifan Hongbdb32012020-05-07 12:38:53 -0700422
Doug Zongker5fad2032014-02-24 08:13:45 -0800423 def WriteRawImage(self, mount_point, fn, mapfn=None):
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700424 """Write the given package file into the partition for the given
425 mount point."""
Doug Zongkerb4c7d322010-07-01 15:30:11 -0700426
Tao Bao34b47bf2015-06-22 19:17:41 -0700427 fstab = self.fstab
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700428 if fstab:
429 p = fstab[mount_point]
Kelvin Zhang782b7522023-10-31 19:33:55 -0700430 partition_type = PARTITION_TYPES[p.fs_type]
Yifan Hongbdb32012020-05-07 12:38:53 -0700431 device = self._GetSlotSuffixDeviceForEntry(p)
432 args = {'device': device, 'fn': fn}
Elliott Hughes305b0882016-06-15 17:04:54 -0700433 if partition_type == "EMMC":
Doug Zongker5fad2032014-02-24 08:13:45 -0800434 if mapfn:
435 args["map"] = mapfn
436 self.script.append(
Yifan Hongbdb32012020-05-07 12:38:53 -0700437 'package_extract_file("%(fn)s", %(device)s, "%(map)s");' % args)
Doug Zongker5fad2032014-02-24 08:13:45 -0800438 else:
439 self.script.append(
Yifan Hongbdb32012020-05-07 12:38:53 -0700440 'package_extract_file("%(fn)s", %(device)s);' % args)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700441 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700442 raise ValueError(
443 "don't know how to write \"%s\" partitions" % p.fs_type)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700444
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700445 def AppendExtra(self, extra):
446 """Append text verbatim to the output script."""
447 self.script.append(extra)
448
Michael Runge63f01de2014-10-28 19:24:19 -0700449 def Unmount(self, mount_point):
Dan Albert8b72aef2015-03-23 19:13:21 -0700450 self.script.append('unmount("%s");' % mount_point)
451 self.mounts.remove(mount_point)
Michael Runge63f01de2014-10-28 19:24:19 -0700452
Doug Zongker14833602010-02-02 13:12:04 -0800453 def UnmountAll(self):
454 for p in sorted(self.mounts):
455 self.script.append('unmount("%s");' % (p,))
456 self.mounts = set()
457
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700458 def AddToZip(self, input_zip, output_zip, input_path=None):
459 """Write the accumulated script to the output_zip file. input_zip
460 is used as the source for the 'updater' binary needed to run
461 script. If input_path is not None, it will be used as a local
462 path for the binary instead of input_zip."""
463
Doug Zongker14833602010-02-02 13:12:04 -0800464 self.UnmountAll()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700465
466 common.ZipWriteStr(output_zip, "META-INF/com/google/android/updater-script",
467 "\n".join(self.script) + "\n")
468
469 if input_path is None:
470 data = input_zip.read("OTA/bin/updater")
471 else:
Doug Zongker25568482014-03-03 10:21:27 -0800472 data = open(input_path, "rb").read()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700473 common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-binary",
Dan Albert8b72aef2015-03-23 19:13:21 -0700474 data, perms=0o755)