Report error codes in the OTA update script
Modify the abort() function in the OTA update generation script to report an error code. The recoveryimage will parse the code and write it into last_install for further data analysis. Bug: 28934032 Change-Id: I2d62f81fd352c3102fb84f054972ac0ecb965a21
This commit is contained in:
@@ -73,6 +73,33 @@ OPTIONS = Options()
|
|||||||
# Values for "certificate" in apkcerts that mean special things.
|
# Values for "certificate" in apkcerts that mean special things.
|
||||||
SPECIAL_CERT_STRINGS = ("PRESIGNED", "EXTERNAL")
|
SPECIAL_CERT_STRINGS = ("PRESIGNED", "EXTERNAL")
|
||||||
|
|
||||||
|
class ErrorCode(object):
|
||||||
|
"""Define error_codes for failures that happen during the actual
|
||||||
|
update package installation.
|
||||||
|
|
||||||
|
Error codes 0-999 are reserved for failures before the package
|
||||||
|
installation (i.e. low battery, package verification failure).
|
||||||
|
Detailed code in 'bootable/recovery/error_code.h' """
|
||||||
|
|
||||||
|
SYSTEM_VERIFICATION_FAILURE = 1000
|
||||||
|
SYSTEM_UPDATE_FAILURE = 1001
|
||||||
|
SYSTEM_UNEXPECTED_CONTENTS = 1002
|
||||||
|
SYSTEM_NONZERO_CONTENTS = 1003
|
||||||
|
SYSTEM_RECOVER_FAILURE = 1004
|
||||||
|
VENDOR_VERIFICATION_FAILURE = 2000
|
||||||
|
VENDOR_UPDATE_FAILURE = 2001
|
||||||
|
VENDOR_UNEXPECTED_CONTENTS = 2002
|
||||||
|
VENDOR_NONZERO_CONTENTS = 2003
|
||||||
|
VENDOR_RECOVER_FAILURE = 2004
|
||||||
|
OEM_PROP_MISMATCH = 3000
|
||||||
|
FINGERPRINT_MISMATCH = 3001
|
||||||
|
THUMBPRINT_MISMATCH = 3002
|
||||||
|
OLDER_BUILD = 3003
|
||||||
|
DEVICE_MISMATCH = 3004
|
||||||
|
BAD_PATCH_FILE = 3005
|
||||||
|
INSUFFICIENT_CACHE_SPACE = 3006
|
||||||
|
TUNE_PARTITION_FAILURE = 3007
|
||||||
|
APPLY_PATCH_FAILURE = 3008
|
||||||
|
|
||||||
class ExternalError(RuntimeError):
|
class ExternalError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
@@ -1433,15 +1460,19 @@ class BlockDifference(object):
|
|||||||
script.AppendExtra('check_first_block("%s");' % (self.device,))
|
script.AppendExtra('check_first_block("%s");' % (self.device,))
|
||||||
|
|
||||||
# If version >= 4, try block recovery before abort update
|
# If version >= 4, try block recovery before abort update
|
||||||
|
if partition == "system":
|
||||||
|
code = ErrorCode.SYSTEM_RECOVER_FAILURE
|
||||||
|
else:
|
||||||
|
code = ErrorCode.VENDOR_RECOVER_FAILURE
|
||||||
script.AppendExtra((
|
script.AppendExtra((
|
||||||
'ifelse (block_image_recover("{device}", "{ranges}") && '
|
'ifelse (block_image_recover("{device}", "{ranges}") && '
|
||||||
'block_image_verify("{device}", '
|
'block_image_verify("{device}", '
|
||||||
'package_extract_file("{partition}.transfer.list"), '
|
'package_extract_file("{partition}.transfer.list"), '
|
||||||
'"{partition}.new.dat", "{partition}.patch.dat"), '
|
'"{partition}.new.dat", "{partition}.patch.dat"), '
|
||||||
'ui_print("{partition} recovered successfully."), '
|
'ui_print("{partition} recovered successfully."), '
|
||||||
'abort("{partition} partition fails to recover"));\n'
|
'abort("E{code}: {partition} partition fails to recover"));\n'
|
||||||
'endif;').format(device=self.device, ranges=ranges_str,
|
'endif;').format(device=self.device, ranges=ranges_str,
|
||||||
partition=partition))
|
partition=partition, code=code))
|
||||||
|
|
||||||
# Abort the OTA update. Note that the incremental OTA cannot be applied
|
# Abort the OTA update. Note that the incremental OTA cannot be applied
|
||||||
# even if it may match the checksum of the target partition.
|
# even if it may match the checksum of the target partition.
|
||||||
@@ -1449,8 +1480,13 @@ class BlockDifference(object):
|
|||||||
# unconditionally and damage the partition.
|
# unconditionally and damage the partition.
|
||||||
# b) If version >= 3, it won't even reach here.
|
# b) If version >= 3, it won't even reach here.
|
||||||
else:
|
else:
|
||||||
script.AppendExtra(('abort("%s partition has unexpected contents");\n'
|
if partition == "system":
|
||||||
'endif;') % (partition,))
|
code = ErrorCode.SYSTEM_VERIFICATION_FAILURE
|
||||||
|
else:
|
||||||
|
code = ErrorCode.VENDOR_VERIFICATION_FAILURE
|
||||||
|
script.AppendExtra((
|
||||||
|
'abort("E%d: %s partition has unexpected contents");\n'
|
||||||
|
'endif;') % (code, partition))
|
||||||
|
|
||||||
def _WritePostInstallVerifyScript(self, script):
|
def _WritePostInstallVerifyScript(self, script):
|
||||||
partition = self.partition
|
partition = self.partition
|
||||||
@@ -1470,18 +1506,28 @@ class BlockDifference(object):
|
|||||||
self.device, ranges_str,
|
self.device, ranges_str,
|
||||||
self._HashZeroBlocks(self.tgt.extended.size())))
|
self._HashZeroBlocks(self.tgt.extended.size())))
|
||||||
script.Print('Verified the updated %s image.' % (partition,))
|
script.Print('Verified the updated %s image.' % (partition,))
|
||||||
|
if partition == "system":
|
||||||
|
code = ErrorCode.SYSTEM_NONZERO_CONTENTS
|
||||||
|
else:
|
||||||
|
code = ErrorCode.VENDOR_NONZERO_CONTENTS
|
||||||
script.AppendExtra(
|
script.AppendExtra(
|
||||||
'else\n'
|
'else\n'
|
||||||
' abort("%s partition has unexpected non-zero contents after OTA '
|
' abort("E%d: %s partition has unexpected non-zero contents after '
|
||||||
'update");\n'
|
'OTA update");\n'
|
||||||
'endif;' % (partition,))
|
'endif;' % (code, partition))
|
||||||
else:
|
else:
|
||||||
script.Print('Verified the updated %s image.' % (partition,))
|
script.Print('Verified the updated %s image.' % (partition,))
|
||||||
|
|
||||||
|
if partition == "system":
|
||||||
|
code = ErrorCode.SYSTEM_UNEXPECTED_CONTENTS
|
||||||
|
else:
|
||||||
|
code = ErrorCode.VENDOR_UNEXPECTED_CONTENTS
|
||||||
|
|
||||||
script.AppendExtra(
|
script.AppendExtra(
|
||||||
'else\n'
|
'else\n'
|
||||||
' abort("%s partition has unexpected contents after OTA update");\n'
|
' abort("E%d: %s partition has unexpected contents after OTA '
|
||||||
'endif;' % (partition,))
|
'update");\n'
|
||||||
|
'endif;' % (code, partition))
|
||||||
|
|
||||||
def _WriteUpdate(self, script, output_zip):
|
def _WriteUpdate(self, script, output_zip):
|
||||||
ZipWrite(output_zip,
|
ZipWrite(output_zip,
|
||||||
@@ -1495,11 +1541,16 @@ class BlockDifference(object):
|
|||||||
'{}.patch.dat'.format(self.partition),
|
'{}.patch.dat'.format(self.partition),
|
||||||
compress_type=zipfile.ZIP_STORED)
|
compress_type=zipfile.ZIP_STORED)
|
||||||
|
|
||||||
|
if self.partition == "system":
|
||||||
|
code = ErrorCode.SYSTEM_UPDATE_FAILURE
|
||||||
|
else:
|
||||||
|
code = ErrorCode.VENDOR_UPDATE_FAILURE
|
||||||
|
|
||||||
call = ('block_image_update("{device}", '
|
call = ('block_image_update("{device}", '
|
||||||
'package_extract_file("{partition}.transfer.list"), '
|
'package_extract_file("{partition}.transfer.list"), '
|
||||||
'"{partition}.new.dat", "{partition}.patch.dat") ||\n'
|
'"{partition}.new.dat", "{partition}.patch.dat") ||\n'
|
||||||
' abort("Failed to update {partition} image.");'.format(
|
' abort("E{code}: Failed to update {partition} image.");'.format(
|
||||||
device=self.device, partition=self.partition))
|
device=self.device, partition=self.partition, code=code))
|
||||||
script.AppendExtra(script.WordWrap(call))
|
script.AppendExtra(script.WordWrap(call))
|
||||||
|
|
||||||
def _HashBlocks(self, source, ranges): # pylint: disable=no-self-use
|
def _HashBlocks(self, source, ranges): # pylint: disable=no-self-use
|
||||||
|
@@ -85,14 +85,17 @@ class EdifyGenerator(object):
|
|||||||
raise ValueError("must specify the OEM value")
|
raise ValueError("must specify the OEM value")
|
||||||
if common.OPTIONS.oem_no_mount:
|
if common.OPTIONS.oem_no_mount:
|
||||||
cmd = ('getprop("{name}") == "{value}" || '
|
cmd = ('getprop("{name}") == "{value}" || '
|
||||||
'abort("This package expects the value \\"{value}\\" for '
|
'abort("E{code}: This package expects the value \\"{value}\\" for '
|
||||||
'\\"{name}\\"; this has value \\"" + '
|
'\\"{name}\\"; this has value \\"" + '
|
||||||
'getprop("{name}") + "\\".");').format(name=name, value=value)
|
'getprop("{name}") + "\\".");').format(
|
||||||
|
code=common.ErrorCode.OEM_PROP_MISMATCH,
|
||||||
|
name=name, value=value)
|
||||||
else:
|
else:
|
||||||
cmd = ('file_getprop("/oem/oem.prop", "{name}") == "{value}" || '
|
cmd = ('file_getprop("/oem/oem.prop", "{name}") == "{value}" || '
|
||||||
'abort("This package expects the value \\"{value}\\" for '
|
'abort("E{code}: This package expects the value \\"{value}\\" for '
|
||||||
'\\"{name}\\" on the OEM partition; this has value \\"" + '
|
'\\"{name}\\" on the OEM partition; this has value \\"" + '
|
||||||
'file_getprop("/oem/oem.prop", "{name}") + "\\".");').format(
|
'file_getprop("/oem/oem.prop", "{name}") + "\\".");').format(
|
||||||
|
code=common.ErrorCode.OEM_PROP_MISMATCH,
|
||||||
name=name, value=value)
|
name=name, value=value)
|
||||||
self.script.append(cmd)
|
self.script.append(cmd)
|
||||||
|
|
||||||
@@ -102,9 +105,9 @@ class EdifyGenerator(object):
|
|||||||
raise ValueError("must specify some fingerprints")
|
raise ValueError("must specify some fingerprints")
|
||||||
cmd = (' ||\n '.join([('getprop("ro.build.fingerprint") == "%s"') % i
|
cmd = (' ||\n '.join([('getprop("ro.build.fingerprint") == "%s"') % i
|
||||||
for i in fp]) +
|
for i in fp]) +
|
||||||
' ||\n abort("Package expects build fingerprint of %s; this '
|
' ||\n abort("E%d: Package expects build fingerprint of %s; '
|
||||||
'device has " + getprop("ro.build.fingerprint") + ".");') % (
|
'this device has " + getprop("ro.build.fingerprint") + ".");') % (
|
||||||
" or ".join(fp))
|
common.ErrorCode.FINGERPRINT_MISMATCH, " or ".join(fp))
|
||||||
self.script.append(cmd)
|
self.script.append(cmd)
|
||||||
|
|
||||||
def AssertSomeThumbprint(self, *fp):
|
def AssertSomeThumbprint(self, *fp):
|
||||||
@@ -113,9 +116,9 @@ class EdifyGenerator(object):
|
|||||||
raise ValueError("must specify some thumbprints")
|
raise ValueError("must specify some thumbprints")
|
||||||
cmd = (' ||\n '.join([('getprop("ro.build.thumbprint") == "%s"') % i
|
cmd = (' ||\n '.join([('getprop("ro.build.thumbprint") == "%s"') % i
|
||||||
for i in fp]) +
|
for i in fp]) +
|
||||||
' ||\n abort("Package expects build thumbprint of %s; this '
|
' ||\n abort("E%d: Package expects build thumbprint of %s; this '
|
||||||
'device has " + getprop("ro.build.thumbprint") + ".");') % (
|
'device has " + getprop("ro.build.thumbprint") + ".");') % (
|
||||||
" or ".join(fp))
|
common.ErrorCode.THUMBPRINT_MISMATCH, " or ".join(fp))
|
||||||
self.script.append(cmd)
|
self.script.append(cmd)
|
||||||
|
|
||||||
def AssertOlderBuild(self, timestamp, timestamp_text):
|
def AssertOlderBuild(self, timestamp, timestamp_text):
|
||||||
@@ -123,16 +126,16 @@ class EdifyGenerator(object):
|
|||||||
the given timestamp."""
|
the given timestamp."""
|
||||||
self.script.append(
|
self.script.append(
|
||||||
('(!less_than_int(%s, getprop("ro.build.date.utc"))) || '
|
('(!less_than_int(%s, getprop("ro.build.date.utc"))) || '
|
||||||
'abort("Can\'t install this package (%s) over newer '
|
'abort("E%d: Can\'t install this package (%s) over newer '
|
||||||
'build (" + getprop("ro.build.date") + ").");') % (timestamp,
|
'build (" + getprop("ro.build.date") + ").");') % (timestamp,
|
||||||
timestamp_text))
|
common.ErrorCode.OLDER_BUILD, timestamp_text))
|
||||||
|
|
||||||
def AssertDevice(self, device):
|
def AssertDevice(self, device):
|
||||||
"""Assert that the device identifier is the given string."""
|
"""Assert that the device identifier is the given string."""
|
||||||
cmd = ('getprop("ro.product.device") == "%s" || '
|
cmd = ('getprop("ro.product.device") == "%s" || '
|
||||||
'abort("This package is for \\"%s\\" devices; '
|
'abort("E%d: This package is for \\"%s\\" devices; '
|
||||||
'this is a \\"" + getprop("ro.product.device") + "\\".");') % (
|
'this is a \\"" + getprop("ro.product.device") + "\\".");') % (
|
||||||
device, device)
|
device, common.ErrorCode.DEVICE_MISMATCH, device)
|
||||||
self.script.append(cmd)
|
self.script.append(cmd)
|
||||||
|
|
||||||
def AssertSomeBootloader(self, *bootloaders):
|
def AssertSomeBootloader(self, *bootloaders):
|
||||||
@@ -162,7 +165,8 @@ class EdifyGenerator(object):
|
|||||||
self.script.append(
|
self.script.append(
|
||||||
'apply_patch_check("%s"' % (filename,) +
|
'apply_patch_check("%s"' % (filename,) +
|
||||||
"".join([', "%s"' % (i,) for i in sha1]) +
|
"".join([', "%s"' % (i,) for i in sha1]) +
|
||||||
') || abort("\\"%s\\" has unexpected contents.");' % (filename,))
|
') || abort("E%d: \\"%s\\" has unexpected contents.");' % (
|
||||||
|
common.ErrorCode.BAD_PATCH_FILE, filename))
|
||||||
|
|
||||||
def Verify(self, filename):
|
def Verify(self, filename):
|
||||||
"""Check that the given file (or MTD reference) has one of the
|
"""Check that the given file (or MTD reference) has one of the
|
||||||
@@ -184,8 +188,10 @@ class EdifyGenerator(object):
|
|||||||
"""Check that there's at least 'amount' space that can be made
|
"""Check that there's at least 'amount' space that can be made
|
||||||
available on /cache."""
|
available on /cache."""
|
||||||
self._required_cache = max(self._required_cache, amount)
|
self._required_cache = max(self._required_cache, amount)
|
||||||
self.script.append(('apply_patch_space(%d) || abort("Not enough free space '
|
self.script.append(('apply_patch_space(%d) || abort("E%d: Not enough free '
|
||||||
'on /cache to apply patches.");') % (amount,))
|
'space on /cache to apply patches.");') % (
|
||||||
|
amount,
|
||||||
|
common.ErrorCode.INSUFFICIENT_CACHE_SPACE))
|
||||||
|
|
||||||
def Mount(self, mount_point, mount_options_by_format=""):
|
def Mount(self, mount_point, mount_options_by_format=""):
|
||||||
"""Mount the partition with the given mount_point.
|
"""Mount the partition with the given mount_point.
|
||||||
@@ -235,8 +241,8 @@ class EdifyGenerator(object):
|
|||||||
raise ValueError("Partition %s cannot be tuned\n" % (partition,))
|
raise ValueError("Partition %s cannot be tuned\n" % (partition,))
|
||||||
self.script.append(
|
self.script.append(
|
||||||
'tune2fs(' + "".join(['"%s", ' % (i,) for i in options]) +
|
'tune2fs(' + "".join(['"%s", ' % (i,) for i in options]) +
|
||||||
'"%s") || abort("Failed to tune partition %s");' % (
|
'"%s") || abort("E%d: Failed to tune partition %s");' % (
|
||||||
p.device, partition))
|
p.device, common.ErrorCode.TUNE_PARTITION_FAILURE, partition))
|
||||||
|
|
||||||
def FormatPartition(self, partition):
|
def FormatPartition(self, partition):
|
||||||
"""Format the given partition, specified by its mount point (eg,
|
"""Format the given partition, specified by its mount point (eg,
|
||||||
@@ -298,7 +304,8 @@ class EdifyGenerator(object):
|
|||||||
% (srcfile, tgtfile, tgtsha1, tgtsize)]
|
% (srcfile, tgtfile, tgtsha1, tgtsize)]
|
||||||
for i in range(0, len(patchpairs), 2):
|
for i in range(0, len(patchpairs), 2):
|
||||||
cmd.append(',\0%s,\0package_extract_file("%s")' % patchpairs[i:i+2])
|
cmd.append(',\0%s,\0package_extract_file("%s")' % patchpairs[i:i+2])
|
||||||
cmd.append(') ||\n abort("Failed to apply patch to %s");' % (srcfile,))
|
cmd.append(') ||\n abort("E%d: Failed to apply patch to %s");' % (
|
||||||
|
common.ErrorCode.APPLY_PATCH_FAILURE, srcfile))
|
||||||
cmd = "".join(cmd)
|
cmd = "".join(cmd)
|
||||||
self.script.append(self.WordWrap(cmd))
|
self.script.append(self.WordWrap(cmd))
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user