blob: 664d5cd9e32ccc75294a7aa72d934ec41c69f9cd [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 Zhang513b86e2023-10-27 13:27:07 -070019
20class ErrorCode(object):
21 """Define error_codes for failures that happen during the actual
22 update package installation.
23
24 Error codes 0-999 are reserved for failures before the package
25 installation (i.e. low battery, package verification failure).
26 Detailed code in 'bootable/recovery/error_code.h' """
27
28 SYSTEM_VERIFICATION_FAILURE = 1000
29 SYSTEM_UPDATE_FAILURE = 1001
30 SYSTEM_UNEXPECTED_CONTENTS = 1002
31 SYSTEM_NONZERO_CONTENTS = 1003
32 SYSTEM_RECOVER_FAILURE = 1004
33 VENDOR_VERIFICATION_FAILURE = 2000
34 VENDOR_UPDATE_FAILURE = 2001
35 VENDOR_UNEXPECTED_CONTENTS = 2002
36 VENDOR_NONZERO_CONTENTS = 2003
37 VENDOR_RECOVER_FAILURE = 2004
38 OEM_PROP_MISMATCH = 3000
39 FINGERPRINT_MISMATCH = 3001
40 THUMBPRINT_MISMATCH = 3002
41 OLDER_BUILD = 3003
42 DEVICE_MISMATCH = 3004
43 BAD_PATCH_FILE = 3005
44 INSUFFICIENT_CACHE_SPACE = 3006
45 TUNE_PARTITION_FAILURE = 3007
46 APPLY_PATCH_FAILURE = 3008
47
48
Doug Zongkerc494d7c2009-06-18 08:43:44 -070049class EdifyGenerator(object):
50 """Class to generate scripts in the 'edify' recovery script language
51 used from donut onwards."""
52
Tao Bao34b47bf2015-06-22 19:17:41 -070053 def __init__(self, version, info, fstab=None):
Doug Zongkerc494d7c2009-06-18 08:43:44 -070054 self.script = []
55 self.mounts = set()
Tao Baod8d14be2016-02-04 14:26:02 -080056 self._required_cache = 0
Doug Zongkerc494d7c2009-06-18 08:43:44 -070057 self.version = version
Doug Zongkerb4c7d322010-07-01 15:30:11 -070058 self.info = info
Tao Bao34b47bf2015-06-22 19:17:41 -070059 if fstab is None:
60 self.fstab = self.info.get("fstab", None)
61 else:
62 self.fstab = fstab
Doug Zongkerc494d7c2009-06-18 08:43:44 -070063
Tao Baod8d14be2016-02-04 14:26:02 -080064 @property
65 def required_cache(self):
66 """Return the minimum cache size to apply the update."""
67 return self._required_cache
68
Doug Zongkerc494d7c2009-06-18 08:43:44 -070069 @staticmethod
Dan Albert8b72aef2015-03-23 19:13:21 -070070 def WordWrap(cmd, linelen=80):
Doug Zongkerc494d7c2009-06-18 08:43:44 -070071 """'cmd' should be a function call with null characters after each
72 parameter (eg, "somefun(foo,\0bar,\0baz)"). This function wraps cmd
73 to a given line length, replacing nulls with spaces and/or newlines
74 to format it nicely."""
75 indent = cmd.index("(")+1
76 out = []
77 first = True
78 x = re.compile("^(.{,%d})\0" % (linelen-indent,))
79 while True:
80 if not first:
81 out.append(" " * indent)
82 first = False
83 m = x.search(cmd)
84 if not m:
85 parts = cmd.split("\0", 1)
86 out.append(parts[0]+"\n")
87 if len(parts) == 1:
88 break
89 else:
90 cmd = parts[1]
91 continue
92 out.append(m.group(1)+"\n")
93 cmd = cmd[m.end():]
94
95 return "".join(out).replace("\0", " ").rstrip("\n")
96
97 def AppendScript(self, other):
98 """Append the contents of another script (which should be created
99 with temporary=True) to this one."""
100 self.script.extend(other.script)
101
Tao Bao481bab82017-12-21 11:23:09 -0800102 def AssertOemProperty(self, name, values, oem_no_mount):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800103 """Assert that a property on the OEM paritition matches allowed values."""
Michael Runge6e836112014-04-15 17:40:21 -0700104 if not name:
105 raise ValueError("must specify an OEM property")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800106 if not values:
Michael Runge6e836112014-04-15 17:40:21 -0700107 raise ValueError("must specify the OEM value")
Tao Bao481bab82017-12-21 11:23:09 -0800108
109 if oem_no_mount:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800110 get_prop_command = 'getprop("%s")' % name
Tao Bao8608cde2016-02-25 19:49:55 -0800111 else:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800112 get_prop_command = 'file_getprop("/oem/oem.prop", "%s")' % name
113
114 cmd = ''
115 for value in values:
116 cmd += '%s == "%s" || ' % (get_prop_command, value)
117 cmd += (
118 'abort("E{code}: This package expects the value \\"{values}\\" for '
119 '\\"{name}\\"; this has value \\"" + '
120 '{get_prop_command} + "\\".");').format(
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700121 code=ErrorCode.OEM_PROP_MISMATCH,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800122 get_prop_command=get_prop_command, name=name,
123 values='\\" or \\"'.join(values))
Michael Runge6e836112014-04-15 17:40:21 -0700124 self.script.append(cmd)
125
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700126 def AssertSomeFingerprint(self, *fp):
Doug Zongkeraf845252014-05-09 08:29:05 -0700127 """Assert that the current recovery build fingerprint is one of *fp."""
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700128 if not fp:
129 raise ValueError("must specify some fingerprints")
Dan Albert8b72aef2015-03-23 19:13:21 -0700130 cmd = (' ||\n '.join([('getprop("ro.build.fingerprint") == "%s"') % i
131 for i in fp]) +
Tianjie Xu209db462016-05-24 17:34:52 -0700132 ' ||\n abort("E%d: Package expects build fingerprint of %s; '
133 'this device has " + getprop("ro.build.fingerprint") + ".");') % (
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700134 ErrorCode.FINGERPRINT_MISMATCH, " or ".join(fp))
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700135 self.script.append(cmd)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700136
Michael Runge6e836112014-04-15 17:40:21 -0700137 def AssertSomeThumbprint(self, *fp):
Doug Zongkeraf845252014-05-09 08:29:05 -0700138 """Assert that the current recovery build thumbprint is one of *fp."""
Geremy Condra36bd3652014-02-06 19:45:10 -0800139 if not fp:
Michael Runge6e836112014-04-15 17:40:21 -0700140 raise ValueError("must specify some thumbprints")
Dan Albert8b72aef2015-03-23 19:13:21 -0700141 cmd = (' ||\n '.join([('getprop("ro.build.thumbprint") == "%s"') % i
142 for i in fp]) +
Tianjie Xu209db462016-05-24 17:34:52 -0700143 ' ||\n abort("E%d: Package expects build thumbprint of %s; this '
Dan Albert8b72aef2015-03-23 19:13:21 -0700144 'device has " + getprop("ro.build.thumbprint") + ".");') % (
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700145 ErrorCode.THUMBPRINT_MISMATCH, " or ".join(fp))
Geremy Condra36bd3652014-02-06 19:45:10 -0800146 self.script.append(cmd)
147
Tao Bao3e30d972016-03-15 13:20:19 -0700148 def AssertFingerprintOrThumbprint(self, fp, tp):
149 """Assert that the current recovery build fingerprint is fp, or thumbprint
150 is tp."""
151 cmd = ('getprop("ro.build.fingerprint") == "{fp}" ||\n'
152 ' getprop("ro.build.thumbprint") == "{tp}" ||\n'
153 ' abort("Package expects build fingerprint of {fp} or '
154 'thumbprint of {tp}; this device has a fingerprint of " '
Tao Baod2d01e52017-06-05 12:25:46 -0700155 '+ getprop("ro.build.fingerprint") + " and a thumbprint of " '
Tao Bao3e30d972016-03-15 13:20:19 -0700156 '+ getprop("ro.build.thumbprint") + ".");').format(fp=fp, tp=tp)
157 self.script.append(cmd)
158
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700159 def AssertOlderBuild(self, timestamp, timestamp_text):
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700160 """Assert that the build on the device is older (or the same as)
161 the given timestamp."""
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700162 self.script.append(
163 ('(!less_than_int(%s, getprop("ro.build.date.utc"))) || '
Tianjie Xu209db462016-05-24 17:34:52 -0700164 'abort("E%d: Can\'t install this package (%s) over newer '
Tao Bao76def242017-11-21 09:25:31 -0800165 'build (" + getprop("ro.build.date") + ").");') % (
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700166 timestamp, ErrorCode.OLDER_BUILD, timestamp_text))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700167
168 def AssertDevice(self, device):
169 """Assert that the device identifier is the given string."""
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700170 cmd = ('getprop("ro.product.device") == "%s" || '
Tianjie Xu209db462016-05-24 17:34:52 -0700171 'abort("E%d: This package is for \\"%s\\" devices; '
Dan Albert8b72aef2015-03-23 19:13:21 -0700172 'this is a \\"" + getprop("ro.product.device") + "\\".");') % (
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700173 device, ErrorCode.DEVICE_MISMATCH, device)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700174 self.script.append(cmd)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700175
176 def AssertSomeBootloader(self, *bootloaders):
177 """Asert that the bootloader version is one of *bootloaders."""
178 cmd = ("assert(" +
179 " ||\0".join(['getprop("ro.bootloader") == "%s"' % (b,)
180 for b in bootloaders]) +
181 ");")
Dan Albert8b72aef2015-03-23 19:13:21 -0700182 self.script.append(self.WordWrap(cmd))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700183
184 def ShowProgress(self, frac, dur):
185 """Update the progress bar, advancing it over 'frac' over the next
Doug Zongker881dd402009-09-20 14:03:55 -0700186 'dur' seconds. 'dur' may be zero to advance it via SetProgress
187 commands instead of by time."""
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700188 self.script.append("show_progress(%f, %d);" % (frac, int(dur)))
189
Doug Zongker881dd402009-09-20 14:03:55 -0700190 def SetProgress(self, frac):
191 """Set the position of the progress bar within the chunk defined
192 by the most recent ShowProgress call. 'frac' should be in
193 [0,1]."""
194 self.script.append("set_progress(%f);" % (frac,))
195
Tao Bao51216552018-08-26 11:53:15 -0700196 def PatchCheck(self, filename, *sha1): # pylint: disable=unused-argument
197 """Checks that the given partition has the desired checksum.
198
199 The call to this function is being deprecated in favor of
200 PatchPartitionCheck(). It will try to parse and handle the old format,
201 unless the format is unknown.
202 """
203 tokens = filename.split(':')
204 assert len(tokens) == 6 and tokens[0] == 'EMMC', \
205 "Failed to handle unknown format. Use PatchPartitionCheck() instead."
206 source = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[2], tokens[3])
207 target = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[4], tokens[5])
208 self.PatchPartitionCheck(target, source)
209
210 def PatchPartitionCheck(self, target, source):
211 """Checks whether updater can patch the given partitions.
212
213 It checks the checksums of the given partitions. If none of them matches the
214 expected checksum, updater will additionally look for a backup on /cache.
215 """
Yifan Hongbdb32012020-05-07 12:38:53 -0700216 self._CheckSecondTokenNotSlotSuffixed(target, "PatchPartitionExprCheck")
217 self._CheckSecondTokenNotSlotSuffixed(source, "PatchPartitionExprCheck")
218 self.PatchPartitionExprCheck('"%s"' % target, '"%s"' % source)
219
220 def PatchPartitionExprCheck(self, target_expr, source_expr):
221 """Checks whether updater can patch the given partitions.
222
223 It checks the checksums of the given partitions. If none of them matches the
224 expected checksum, updater will additionally look for a backup on /cache.
225
226 Args:
227 target_expr: an Edify expression that serves as the target arg to
228 patch_partition. Must be evaluated to a string in the form of
229 foo:bar:baz:quux
230 source_expr: an Edify expression that serves as the source arg to
231 patch_partition. Must be evaluated to a string in the form of
232 foo:bar:baz:quux
233 """
Tao Bao51216552018-08-26 11:53:15 -0700234 self.script.append(self.WordWrap((
Yifan Hongbdb32012020-05-07 12:38:53 -0700235 'patch_partition_check({target},\0{source}) ||\n abort('
236 'concat("E{code}: \\"",{target},"\\" or \\"",{source},"\\" has '
237 'unexpected contents."));').format(
238 target=target_expr,
239 source=source_expr,
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700240 code=ErrorCode.BAD_PATCH_FILE)))
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800241
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700242 def CacheFreeSpaceCheck(self, amount):
243 """Check that there's at least 'amount' space that can be made
244 available on /cache."""
Tao Baod8d14be2016-02-04 14:26:02 -0800245 self._required_cache = max(self._required_cache, amount)
Tianjie Xu209db462016-05-24 17:34:52 -0700246 self.script.append(('apply_patch_space(%d) || abort("E%d: Not enough free '
247 'space on /cache to apply patches.");') % (
248 amount,
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700249 ErrorCode.INSUFFICIENT_CACHE_SPACE))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700250
Michael Runge7cd99ba2014-10-22 17:21:48 -0700251 def Mount(self, mount_point, mount_options_by_format=""):
252 """Mount the partition with the given mount_point.
253 mount_options_by_format:
254 [fs_type=option[,option]...[|fs_type=option[,option]...]...]
255 where option is optname[=optvalue]
256 E.g. ext4=barrier=1,nodelalloc,errors=panic|f2fs=errors=recover
257 """
Tao Bao34b47bf2015-06-22 19:17:41 -0700258 fstab = self.fstab
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700259 if fstab:
260 p = fstab[mount_point]
Michael Runge7cd99ba2014-10-22 17:21:48 -0700261 mount_dict = {}
262 if mount_options_by_format is not None:
263 for option in mount_options_by_format.split("|"):
264 if "=" in option:
265 key, value = option.split("=", 1)
266 mount_dict[key] = value
Tao Baodf06e962015-06-10 12:32:41 -0700267 mount_flags = mount_dict.get(p.fs_type, "")
268 if p.context is not None:
269 mount_flags = p.context + ("," + mount_flags if mount_flags else "")
Yifan Hongbdb32012020-05-07 12:38:53 -0700270 self.script.append('mount("%s", "%s", %s, "%s", "%s");' % (
Abhishek Nigamb148ac22023-11-08 02:19:31 +0000271 p.fs_type, common.PARTITION_TYPES[p.fs_type],
Yifan Hongbdb32012020-05-07 12:38:53 -0700272 self._GetSlotSuffixDeviceForEntry(p),
Tao Baodf06e962015-06-10 12:32:41 -0700273 p.mount_point, mount_flags))
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700274 self.mounts.add(p.mount_point)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700275
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700276 def Comment(self, comment):
277 """Write a comment into the update script."""
278 self.script.append("")
279 for i in comment.split("\n"):
280 self.script.append("# " + i)
281 self.script.append("")
282
283 def Print(self, message):
284 """Log a message to the screen (if the logs are visible)."""
285 self.script.append('ui_print("%s");' % (message,))
286
Michael Runge3e286642014-11-21 00:46:03 -0800287 def TunePartition(self, partition, *options):
Tao Bao34b47bf2015-06-22 19:17:41 -0700288 fstab = self.fstab
Michael Runge3e286642014-11-21 00:46:03 -0800289 if fstab:
290 p = fstab[partition]
Dan Albert8b72aef2015-03-23 19:13:21 -0700291 if p.fs_type not in ("ext2", "ext3", "ext4"):
Michael Runge3e286642014-11-21 00:46:03 -0800292 raise ValueError("Partition %s cannot be tuned\n" % (partition,))
Dan Albert8b72aef2015-03-23 19:13:21 -0700293 self.script.append(
294 'tune2fs(' + "".join(['"%s", ' % (i,) for i in options]) +
Yifan Hongbdb32012020-05-07 12:38:53 -0700295 '%s) || abort("E%d: Failed to tune partition %s");' % (
296 self._GetSlotSuffixDeviceForEntry(p),
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700297 ErrorCode.TUNE_PARTITION_FAILURE, partition))
Michael Runge3e286642014-11-21 00:46:03 -0800298
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700299 def FormatPartition(self, partition):
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700300 """Format the given partition, specified by its mount point (eg,
301 "/system")."""
302
Tao Bao34b47bf2015-06-22 19:17:41 -0700303 fstab = self.fstab
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700304 if fstab:
305 p = fstab[partition]
Yifan Hongbdb32012020-05-07 12:38:53 -0700306 self.script.append('format("%s", "%s", %s, "%s", "%s");' %
Abhishek Nigamb148ac22023-11-08 02:19:31 +0000307 (p.fs_type, common.PARTITION_TYPES[p.fs_type],
Yifan Hongbdb32012020-05-07 12:38:53 -0700308 self._GetSlotSuffixDeviceForEntry(p),
309 p.length, p.mount_point))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700310
Doug Zongker5fad2032014-02-24 08:13:45 -0800311 def WipeBlockDevice(self, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700312 if partition not in ("/system", "/vendor"):
313 raise ValueError(("WipeBlockDevice doesn't work on %s\n") % (partition,))
Tao Bao34b47bf2015-06-22 19:17:41 -0700314 fstab = self.fstab
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700315 size = self.info.get(partition.lstrip("/") + "_size", None)
Yifan Hongbdb32012020-05-07 12:38:53 -0700316 device = self._GetSlotSuffixDeviceForEntry(fstab[partition])
Doug Zongker5fad2032014-02-24 08:13:45 -0800317
Yifan Hongbdb32012020-05-07 12:38:53 -0700318 self.script.append('wipe_block_device(%s, %s);' % (device, size))
Doug Zongker5fad2032014-02-24 08:13:45 -0800319
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700320 def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs):
321 """Apply binary patches (in *patchpairs) to the given srcfile to
322 produce tgtfile (which may be "-" to indicate overwriting the
Tao Bao51216552018-08-26 11:53:15 -0700323 source file.
324
325 This edify function is being deprecated in favor of PatchPartition(). It
326 will try to redirect calls to PatchPartition() if possible. On unknown /
327 invalid inputs, raises an exception.
328 """
329 tokens = srcfile.split(':')
330 assert (len(tokens) == 6 and tokens[0] == 'EMMC' and tgtfile == '-' and
331 len(patchpairs) == 2), \
332 "Failed to handle unknown format. Use PatchPartition() instead."
333
Kelvin Zhangc693d952020-07-22 19:21:22 -0400334 # Also validity check the args.
Tao Bao51216552018-08-26 11:53:15 -0700335 assert tokens[3] == patchpairs[0], \
336 "Found mismatching values for source SHA-1: {} vs {}".format(
337 tokens[3], patchpairs[0])
338 assert int(tokens[4]) == tgtsize, \
339 "Found mismatching values for target size: {} vs {}".format(
340 tokens[4], tgtsize)
341 assert tokens[5] == tgtsha1, \
342 "Found mismatching values for target SHA-1: {} vs {}".format(
343 tokens[5], tgtsha1)
344
345 source = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[2], tokens[3])
346 target = '{}:{}:{}:{}'.format(tokens[0], tokens[1], tokens[4], tokens[5])
347 patch = patchpairs[1]
348 self.PatchPartition(target, source, patch)
349
350 def PatchPartition(self, target, source, patch):
Yifan Hongbdb32012020-05-07 12:38:53 -0700351 """
352 Applies the patch to the source partition and writes it to target.
353
354 Args:
355 target: the target arg to patch_partition. Must be in the form of
356 foo:bar:baz:quux
357 source: the source arg to patch_partition. Must be in the form of
358 foo:bar:baz:quux
359 patch: the patch arg to patch_partition. Must be an unquoted string.
360 """
361 self._CheckSecondTokenNotSlotSuffixed(target, "PatchPartitionExpr")
362 self._CheckSecondTokenNotSlotSuffixed(source, "PatchPartitionExpr")
363 self.PatchPartitionExpr('"%s"' % target, '"%s"' % source, '"%s"' % patch)
364
365 def PatchPartitionExpr(self, target_expr, source_expr, patch_expr):
366 """
367 Applies the patch to the source partition and writes it to target.
368
369 Args:
370 target_expr: an Edify expression that serves as the target arg to
371 patch_partition. Must be evaluated to a string in the form of
372 foo:bar:baz:quux
373 source_expr: an Edify expression that serves as the source arg to
374 patch_partition. Must be evaluated to a string in the form of
375 foo:bar:baz:quux
376 patch_expr: an Edify expression that serves as the patch arg to
377 patch_partition. Must be evaluated to a string.
378 """
Tao Bao51216552018-08-26 11:53:15 -0700379 self.script.append(self.WordWrap((
Yifan Hongbdb32012020-05-07 12:38:53 -0700380 'patch_partition({target},\0{source},\0'
381 'package_extract_file({patch})) ||\n'
382 ' abort(concat('
383 ' "E{code}: Failed to apply patch to ",{source}));').format(
384 target=target_expr,
385 source=source_expr,
386 patch=patch_expr,
Kelvin Zhang513b86e2023-10-27 13:27:07 -0700387 code=ErrorCode.APPLY_PATCH_FAILURE)))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700388
Yifan Hongbdb32012020-05-07 12:38:53 -0700389 def _GetSlotSuffixDeviceForEntry(self, entry=None):
390 """
391 Args:
392 entry: the fstab entry of device "foo"
393 Returns:
394 An edify expression. Caller must not quote result.
395 If foo is slot suffixed, it returns
396 'add_slot_suffix("foo")'
397 Otherwise it returns
398 '"foo"' (quoted)
399 """
400 assert entry is not None
401 if entry.slotselect:
402 return 'add_slot_suffix("%s")' % entry.device
403 return '"%s"' % entry.device
404
405 def _CheckSecondTokenNotSlotSuffixed(self, s, fn):
406 lst = s.split(':')
P.Adarsh Reddy1ed2d662020-07-01 00:30:59 +0530407 assert(len(lst) == 4), "{} does not contain 4 tokens".format(s)
Yifan Hongbdb32012020-05-07 12:38:53 -0700408 if self.fstab:
P.Adarsh Reddy1ed2d662020-07-01 00:30:59 +0530409 entry = common.GetEntryForDevice(self.fstab, lst[1])
Yifan Hongbdb32012020-05-07 12:38:53 -0700410 if entry is not None:
411 assert not entry.slotselect, \
P.Adarsh Reddy1ed2d662020-07-01 00:30:59 +0530412 "Use %s because %s is slot suffixed" % (fn, lst[1])
Yifan Hongbdb32012020-05-07 12:38:53 -0700413
Doug Zongker5fad2032014-02-24 08:13:45 -0800414 def WriteRawImage(self, mount_point, fn, mapfn=None):
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700415 """Write the given package file into the partition for the given
416 mount point."""
Doug Zongkerb4c7d322010-07-01 15:30:11 -0700417
Tao Bao34b47bf2015-06-22 19:17:41 -0700418 fstab = self.fstab
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700419 if fstab:
420 p = fstab[mount_point]
Abhishek Nigamb148ac22023-11-08 02:19:31 +0000421 partition_type = common.PARTITION_TYPES[p.fs_type]
Yifan Hongbdb32012020-05-07 12:38:53 -0700422 device = self._GetSlotSuffixDeviceForEntry(p)
423 args = {'device': device, 'fn': fn}
Elliott Hughes305b0882016-06-15 17:04:54 -0700424 if partition_type == "EMMC":
Doug Zongker5fad2032014-02-24 08:13:45 -0800425 if mapfn:
426 args["map"] = mapfn
427 self.script.append(
Yifan Hongbdb32012020-05-07 12:38:53 -0700428 'package_extract_file("%(fn)s", %(device)s, "%(map)s");' % args)
Doug Zongker5fad2032014-02-24 08:13:45 -0800429 else:
430 self.script.append(
Yifan Hongbdb32012020-05-07 12:38:53 -0700431 'package_extract_file("%(fn)s", %(device)s);' % args)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700432 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700433 raise ValueError(
434 "don't know how to write \"%s\" partitions" % p.fs_type)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700435
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700436 def AppendExtra(self, extra):
437 """Append text verbatim to the output script."""
438 self.script.append(extra)
439
Michael Runge63f01de2014-10-28 19:24:19 -0700440 def Unmount(self, mount_point):
Dan Albert8b72aef2015-03-23 19:13:21 -0700441 self.script.append('unmount("%s");' % mount_point)
442 self.mounts.remove(mount_point)
Michael Runge63f01de2014-10-28 19:24:19 -0700443
Doug Zongker14833602010-02-02 13:12:04 -0800444 def UnmountAll(self):
445 for p in sorted(self.mounts):
446 self.script.append('unmount("%s");' % (p,))
447 self.mounts = set()
448
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700449 def AddToZip(self, input_zip, output_zip, input_path=None):
450 """Write the accumulated script to the output_zip file. input_zip
451 is used as the source for the 'updater' binary needed to run
452 script. If input_path is not None, it will be used as a local
453 path for the binary instead of input_zip."""
454
Doug Zongker14833602010-02-02 13:12:04 -0800455 self.UnmountAll()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700456
457 common.ZipWriteStr(output_zip, "META-INF/com/google/android/updater-script",
458 "\n".join(self.script) + "\n")
459
460 if input_path is None:
461 data = input_zip.read("OTA/bin/updater")
462 else:
Doug Zongker25568482014-03-03 10:21:27 -0800463 data = open(input_path, "rb").read()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700464 common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-binary",
Dan Albert8b72aef2015-03-23 19:13:21 -0700465 data, perms=0o755)