diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/ButtonlessDfuImpl.java b/dfu/src/main/java/no/nordicsemi/android/dfu/ButtonlessDfuImpl.java index 8a8d64e..31a0cb0 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/ButtonlessDfuImpl.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/ButtonlessDfuImpl.java @@ -9,6 +9,7 @@ import no.nordicsemi.android.dfu.internal.exception.DfuException; import no.nordicsemi.android.dfu.internal.exception.RemoteDfuException; import no.nordicsemi.android.dfu.internal.exception.UnknownResponseException; import no.nordicsemi.android.dfu.internal.exception.UploadAbortedException; +import no.nordicsemi.android.error.SecureDfuError; /** * A base class for buttonless service implementations made for Secure and in the future for Non-Secure DFU. @@ -16,8 +17,6 @@ import no.nordicsemi.android.dfu.internal.exception.UploadAbortedException; /* package */ abstract class ButtonlessDfuImpl extends BaseButtonlessDfuImpl { private static final int DFU_STATUS_SUCCESS = 1; - private static final int ERROR_OP_CODE_NOT_SUPPORTED = 2; - private static final int ERROR_OPERATION_FAILED = 4; private static final int OP_CODE_ENTER_BOOTLOADER_KEY = 0x01; private static final int OP_CODE_RESPONSE_CODE_KEY = 0x20; @@ -127,10 +126,10 @@ import no.nordicsemi.android.dfu.internal.exception.UploadAbortedException; mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, e.getMessage()); mService.terminateConnection(gatt, error); } catch (final RemoteDfuException e) { - final int error = DfuBaseService.ERROR_REMOTE_MASK | e.getErrorNumber(); + final int error = DfuBaseService.ERROR_REMOTE_TYPE_SECURE_BUTTONLESS | e.getErrorNumber(); loge(e.getMessage()); - mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, String.format("Remote DFU error: %s", parse(error))); - mService.terminateConnection(gatt, error); + mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, String.format("Remote DFU error: %s", SecureDfuError.parseButtonlessError(error))); + mService.terminateConnection(gatt, error | DfuBaseService.ERROR_REMOTE_MASK); } } @@ -144,16 +143,8 @@ import no.nordicsemi.android.dfu.internal.exception.UploadAbortedException; */ private int getStatusCode(final byte[] response, final int request) throws UnknownResponseException { if (response == null || response.length < 3 || response[0] != OP_CODE_RESPONSE_CODE_KEY || response[1] != request || - (response[2] != DFU_STATUS_SUCCESS && response[2] != ERROR_OP_CODE_NOT_SUPPORTED && response[2] != ERROR_OPERATION_FAILED)) + (response[2] != DFU_STATUS_SUCCESS && response[2] != SecureDfuError.BUTTONLESS_ERROR_OP_CODE_NOT_SUPPORTED && response[2] != SecureDfuError.BUTTONLESS_ERROR_OPERATION_FAILED)) throw new UnknownResponseException("Invalid response received", response, OP_CODE_RESPONSE_CODE_KEY, request); return response[2]; } - - private static String parse(final int error) { - switch (error & (~DfuBaseService.ERROR_REMOTE_MASK)) { - case ERROR_OP_CODE_NOT_SUPPORTED: return "REMOTE DFU OP CODE NOT SUPPORTED"; - case ERROR_OPERATION_FAILED: return "REMOTE DFU OPERATION FAILED"; - default: return "UNKNOWN (" + error + ")"; - } - } } diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java b/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java index 74c9c19..d576779 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java @@ -422,7 +422,7 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres /** * The broadcast error message contains the following extras: * */ @@ -463,12 +463,6 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres * Thrown when the service discovery has finished but the DFU service has not been found. The device does not support DFU of is not in DFU mode. */ public static final int ERROR_SERVICE_NOT_FOUND = ERROR_MASK | 0x06; - /** - * Thrown when the required DFU service has been found but at least one of the DFU characteristics is absent. - * @deprecated This error will no longer be thrown. {@link #ERROR_SERVICE_NOT_FOUND} will be thrown instead. - */ - @Deprecated - public static final int ERROR_CHARACTERISTICS_NOT_FOUND = ERROR_MASK | 0x07; /** * Thrown when unknown response has been obtained from the target. The DFU target must follow specification. */ @@ -498,9 +492,14 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres */ public static final int ERROR_DEVICE_NOT_BONDED = ERROR_MASK | 0x0E; /** - * Flag set when the DFU target returned a DFU error. Look for DFU specification to get error codes. + * Flag set when the DFU target returned a DFU error. Look for DFU specification to get error codes. The error code is binary OR-ed with one of: + * {@link #ERROR_REMOTE_TYPE_LEGACY}, {@link #ERROR_REMOTE_TYPE_SECURE} or {@link #ERROR_REMOTE_TYPE_SECURE_EXTENDED}. */ public static final int ERROR_REMOTE_MASK = 0x2000; + public static final int ERROR_REMOTE_TYPE_LEGACY = 0x0100; + public static final int ERROR_REMOTE_TYPE_SECURE = 0x0200; + public static final int ERROR_REMOTE_TYPE_SECURE_EXTENDED = 0x0400; + public static final int ERROR_REMOTE_TYPE_SECURE_BUTTONLESS = 0x0800; /** * The flag set when one of {@link android.bluetooth.BluetoothGattCallback} methods was called with status other than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}. */ diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceListenerHelper.java b/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceListenerHelper.java index bc401fa..4febd17 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceListenerHelper.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceListenerHelper.java @@ -247,6 +247,12 @@ public class DfuServiceListenerHelper { if (deviceListener != null) deviceListener.onError(address, error, errorType, GattError.parseConnectionError(error)); break; + case DfuBaseService.ERROR_TYPE_DFU_REMOTE: + if (globalListener != null) + globalListener.onError(address, error, errorType, GattError.parseDfuRemoteError(error)); + if (deviceListener != null) + deviceListener.onError(address, error, errorType, GattError.parseDfuRemoteError(error)); + break; default: if (globalListener != null) globalListener.onError(address, error, errorType, GattError.parse(error)); diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/LegacyDfuImpl.java b/dfu/src/main/java/no/nordicsemi/android/dfu/LegacyDfuImpl.java index d58f65f..18b267e 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/LegacyDfuImpl.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/LegacyDfuImpl.java @@ -530,14 +530,14 @@ import no.nordicsemi.android.error.LegacyDfuError; mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Reset request sent"); mService.terminateConnection(gatt, error); } catch (final RemoteDfuException e) { - final int error = DfuBaseService.ERROR_REMOTE_MASK | e.getErrorNumber(); - loge(e.getMessage()); + final int error = DfuBaseService.ERROR_REMOTE_TYPE_LEGACY | e.getErrorNumber(); + loge(e.getMessage() + ": " + LegacyDfuError.parse(error)); mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, String.format("Remote DFU error: %s", LegacyDfuError.parse(error))); logi("Sending Reset command (Op Code = 6)"); writeOpCode(mControlPointCharacteristic, OP_CODE_RESET); mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Reset request sent"); - mService.terminateConnection(gatt, error); + mService.terminateConnection(gatt, error | DfuBaseService.ERROR_REMOTE_MASK); } } diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java b/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java index 092ff85..eb94540 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java @@ -223,19 +223,19 @@ import no.nordicsemi.android.error.SecureDfuError; mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, e.getMessage()); mService.terminateConnection(gatt, error); } catch (final RemoteDfuException e) { - final int error = DfuBaseService.ERROR_REMOTE_MASK | e.getErrorNumber(); - loge(e.getMessage()); + final int error = DfuBaseService.ERROR_REMOTE_TYPE_SECURE | e.getErrorNumber(); + loge(e.getMessage() + ": " + SecureDfuError.parse(error)); mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, String.format("Remote DFU error: %s", SecureDfuError.parse(error))); // For the Extended Error more details can be obtained on some devices. if (e instanceof RemoteDfuExtendedErrorException) { final RemoteDfuExtendedErrorException ee = (RemoteDfuExtendedErrorException) e; - final int extendedError = ee.getExtendedErrorNumber(); - logi("Extended Error details: " + SecureDfuError.parseExtendedError(extendedError)); - mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, "Details: " + SecureDfuError.parseExtendedError(extendedError) + " (Code = " + extendedError + ")"); - mService.terminateConnection(gatt, extendedError & DfuBaseService.ERROR_REMOTE_MASK); + final int extendedError = DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | ee.getExtendedErrorNumber(); + loge("Extended Error details: " + SecureDfuError.parseExtendedError(extendedError)); + mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, "Details: " + SecureDfuError.parseExtendedError(extendedError) + " (Code = " + ee.getExtendedErrorNumber() + ")"); + mService.terminateConnection(gatt, extendedError | DfuBaseService.ERROR_REMOTE_MASK); } else { - mService.terminateConnection(gatt, error); + mService.terminateConnection(gatt, error | DfuBaseService.ERROR_REMOTE_MASK); } } } diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/internal/exception/RemoteDfuExtendedErrorException.java b/dfu/src/main/java/no/nordicsemi/android/dfu/internal/exception/RemoteDfuExtendedErrorException.java index 26a1e7d..98e30d7 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/internal/exception/RemoteDfuExtendedErrorException.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/internal/exception/RemoteDfuExtendedErrorException.java @@ -44,6 +44,6 @@ public class RemoteDfuExtendedErrorException extends RemoteDfuException { @Override public String getMessage() { - return super.getMessage() + " (error " + SecureDfuError.EXTENDED_ERROR + "." + mError + ")"; + return super.getMessage() + " (extended error " + mError + ")"; } } diff --git a/dfu/src/main/java/no/nordicsemi/android/error/GattError.java b/dfu/src/main/java/no/nordicsemi/android/error/GattError.java index 527c33d..61db52a 100644 --- a/dfu/src/main/java/no/nordicsemi/android/error/GattError.java +++ b/dfu/src/main/java/no/nordicsemi/android/error/GattError.java @@ -29,6 +29,7 @@ import no.nordicsemi.android.dfu.DfuBaseService; /** * Parses the error numbers according to the gatt_api.h file from bluedroid stack. * See: https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/android-5.1.0_r1/stack/include/gatt_api.h (and other versions) for details. + * See also: https://android.googlesource.com/platform/external/libnfc-nci/+/master/src/include/hcidefs.h#447 for other possible HCI errors. */ public class GattError { // Starts at line 106 of gatt_api.h file @@ -161,8 +162,6 @@ public class GattError { case DfuBaseService.ERROR_SERVICE_DISCOVERY_NOT_STARTED: return "DFU SERVICE DISCOVERY NOT STARTED"; case DfuBaseService.ERROR_SERVICE_NOT_FOUND: - return "DFU SERVICE NOT FOUND"; - case DfuBaseService.ERROR_CHARACTERISTICS_NOT_FOUND: return "DFU CHARACTERISTICS NOT FOUND"; case DfuBaseService.ERROR_INVALID_RESPONSE: return "DFU INVALID RESPONSE"; @@ -171,17 +170,30 @@ public class GattError { case DfuBaseService.ERROR_BLUETOOTH_DISABLED: return "BLUETOOTH ADAPTER DISABLED"; case DfuBaseService.ERROR_INIT_PACKET_REQUIRED: - return "INIT PACKET REQUIRED"; + return "DFU INIT PACKET REQUIRED"; case DfuBaseService.ERROR_FILE_SIZE_INVALID: - return "DFU FILE NOT WORD ALIGNED"; + return "DFU INIT PACKET REQUIRED"; + case DfuBaseService.ERROR_CRC_ERROR: + return "DFU CRC ERROR"; case DfuBaseService.ERROR_DEVICE_NOT_BONDED: return "DFU DEVICE NOT BONDED"; default: - // Deprecated: use Legacy or SecureDfuError parser - if ((DfuBaseService.ERROR_REMOTE_MASK & error) > 0) { - return LegacyDfuError.parse(error); - } + return "UNKNOWN (" + error + ")"; + } + } + + public static String parseDfuRemoteError(final int error) { + switch (error & (DfuBaseService.ERROR_REMOTE_TYPE_LEGACY | DfuBaseService.ERROR_REMOTE_TYPE_SECURE | DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | DfuBaseService.ERROR_REMOTE_TYPE_SECURE_BUTTONLESS)) { + case DfuBaseService.ERROR_REMOTE_TYPE_LEGACY: + return LegacyDfuError.parse(error); + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE: + return SecureDfuError.parse(error); + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED: + return SecureDfuError.parseExtendedError(error); + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_BUTTONLESS: + return SecureDfuError.parseButtonlessError(error); + default: + return "UNKNOWN (" + error + ")"; } - return "UNKNOWN (" + error + ")"; } } diff --git a/dfu/src/main/java/no/nordicsemi/android/error/LegacyDfuError.java b/dfu/src/main/java/no/nordicsemi/android/error/LegacyDfuError.java index d0cb9f6..dbb8e83 100644 --- a/dfu/src/main/java/no/nordicsemi/android/error/LegacyDfuError.java +++ b/dfu/src/main/java/no/nordicsemi/android/error/LegacyDfuError.java @@ -34,13 +34,14 @@ public final class LegacyDfuError { public static final int OPERATION_FAILED = 6; public static String parse(final int error) { - switch (error & (~DfuBaseService.ERROR_REMOTE_MASK)) { - case INVALID_STATE: return "REMOTE DFU INVALID STATE"; - case NOT_SUPPORTED: return "REMOTE DFU NOT SUPPORTED"; - case DATA_SIZE_EXCEEDS_LIMIT: return "REMOTE DFU DATA SIZE EXCEEDS LIMIT"; - case CRC_ERROR: return "REMOTE DFU INVALID CRC ERROR"; - case OPERATION_FAILED: return "REMOTE DFU OPERATION FAILED"; - default: return "UNKNOWN (" + error + ")"; + switch (error) { + case DfuBaseService.ERROR_REMOTE_TYPE_LEGACY | INVALID_STATE: return "INVALID STATE"; + case DfuBaseService.ERROR_REMOTE_TYPE_LEGACY | NOT_SUPPORTED: return "NOT SUPPORTED"; + case DfuBaseService.ERROR_REMOTE_TYPE_LEGACY | DATA_SIZE_EXCEEDS_LIMIT: return "DATA SIZE EXCEEDS LIMIT"; + case DfuBaseService.ERROR_REMOTE_TYPE_LEGACY | CRC_ERROR: return "INVALID CRC ERROR"; + case DfuBaseService.ERROR_REMOTE_TYPE_LEGACY | OPERATION_FAILED: return "OPERATION FAILED"; + default: + return "UNKNOWN (" + error + ")"; } } } diff --git a/dfu/src/main/java/no/nordicsemi/android/error/SecureDfuError.java b/dfu/src/main/java/no/nordicsemi/android/error/SecureDfuError.java index 5b60912..6447434 100644 --- a/dfu/src/main/java/no/nordicsemi/android/error/SecureDfuError.java +++ b/dfu/src/main/java/no/nordicsemi/android/error/SecureDfuError.java @@ -50,35 +50,50 @@ public final class SecureDfuError { public static final int EXT_ERROR_VERIFICATION_FAILED = 0x0C; public static final int EXT_ERROR_INSUFFICIENT_SPACE = 0x0D; + // public static final int BUTTONLESS_SUCCESS = 1; + public static final int BUTTONLESS_ERROR_OP_CODE_NOT_SUPPORTED = 2; + public static final int BUTTONLESS_ERROR_OPERATION_FAILED = 4; + public static String parse(final int error) { - switch (error & (~DfuBaseService.ERROR_REMOTE_MASK)) { - case OP_CODE_NOT_SUPPORTED: return "REMOTE DFU OP CODE NOT SUPPORTED"; - case INVALID_PARAM: return "REMOTE DFU INVALID PARAM"; - case INSUFFICIENT_RESOURCES: return "REMOTE DFU INSUFFICIENT RESOURCES"; - case INVALID_OBJECT: return "REMOTE DFU INVALID OBJECT"; - case UNSUPPORTED_TYPE: return "REMOTE DFU UNSUPPORTED TYPE"; - case OPERATION_NOT_PERMITTED: return "REMOTE DFU OPERATION NOT PERMITTED"; - case OPERATION_FAILED: return "REMOTE DFU OPERATION FAILED"; - case EXTENDED_ERROR: return "REMOTE DFU EXTENDED ERROR"; - default: return "UNKNOWN (" + error + ")"; + switch (error) { + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE | OP_CODE_NOT_SUPPORTED: return "OP CODE NOT SUPPORTED"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE | INVALID_PARAM: return "INVALID PARAM"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE | INSUFFICIENT_RESOURCES: return "INSUFFICIENT RESOURCES"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE | INVALID_OBJECT: return "INVALID OBJECT"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE | UNSUPPORTED_TYPE: return "UNSUPPORTED TYPE"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE | OPERATION_NOT_PERMITTED: return "OPERATION NOT PERMITTED"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE | OPERATION_FAILED: return "OPERATION FAILED"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE | EXTENDED_ERROR: return "EXTENDED ERROR"; + default: + return "UNKNOWN (" + error + ")"; } } public static String parseExtendedError(final int error) { switch (error) { - case EXT_ERROR_WRONG_COMMAND_FORMAT: return "Wrong command format"; - case EXT_ERROR_UNKNOWN_COMMAND: return "Unknown command"; - case EXT_ERROR_INIT_COMMAND_INVALID: return "Init command invalid"; - case EXT_ERROR_FW_VERSION_FAILURE: return "FW version failure"; - case EXT_ERROR_HW_VERSION_FAILURE: return "HW version failure"; - case EXT_ERROR_SD_VERSION_FAILURE: return "SD version failure"; - case EXT_ERROR_SIGNATURE_MISSING : return "Signature mismatch"; - case EXT_ERROR_WRONG_HASH_TYPE: return "Wrong hash type"; - case EXT_ERROR_HASH_FAILED: return "Hash failed"; - case EXT_ERROR_WRONG_SIGNATURE_TYPE: return "Wring signature type"; - case EXT_ERROR_VERIFICATION_FAILED: return "Verification failed"; - case EXT_ERROR_INSUFFICIENT_SPACE: return "Insufficient space"; - default: return "Reserved for future use"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_WRONG_COMMAND_FORMAT: return "Wrong command format"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_UNKNOWN_COMMAND: return "Unknown command"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_INIT_COMMAND_INVALID: return "Init command invalid"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_FW_VERSION_FAILURE: return "FW version failure"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_HW_VERSION_FAILURE: return "HW version failure"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_SD_VERSION_FAILURE: return "SD version failure"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_SIGNATURE_MISSING : return "Signature mismatch"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_WRONG_HASH_TYPE: return "Wrong hash type"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_HASH_FAILED: return "Hash failed"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_WRONG_SIGNATURE_TYPE: return "Wring signature type"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_VERIFICATION_FAILED: return "Verification failed"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_EXTENDED | EXT_ERROR_INSUFFICIENT_SPACE: return "Insufficient space"; + default: + return "Reserved for future use"; + } + } + + public static String parseButtonlessError(final int error) { + switch (error) { + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_BUTTONLESS | BUTTONLESS_ERROR_OP_CODE_NOT_SUPPORTED: return "OP CODE NOT SUPPORTED"; + case DfuBaseService.ERROR_REMOTE_TYPE_SECURE_BUTTONLESS | BUTTONLESS_ERROR_OPERATION_FAILED: return "OPERATION FAILED"; + default: + return "UNKNOWN (" + error + ")"; } } }