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 aaf97b0..79a213b 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java @@ -38,6 +38,7 @@ import no.nordicsemi.android.dfu.internal.ArchiveInputStream; import no.nordicsemi.android.dfu.internal.exception.DeviceDisconnectedException; import no.nordicsemi.android.dfu.internal.exception.DfuException; import no.nordicsemi.android.dfu.internal.exception.RemoteDfuException; +import no.nordicsemi.android.dfu.internal.exception.RemoteDfuExtendedErrorException; import no.nordicsemi.android.dfu.internal.exception.UnknownResponseException; import no.nordicsemi.android.dfu.internal.exception.UploadAbortedException; import no.nordicsemi.android.error.SecureDfuError; @@ -59,7 +60,6 @@ import no.nordicsemi.android.error.SecureDfuError; private static final int OP_CODE_PACKET_RECEIPT_NOTIF_REQ_KEY = 0x02; private static final int OP_CODE_CALCULATE_CHECKSUM_KEY = 0x03; private static final int OP_CODE_EXECUTE_KEY = 0x04; - private static final int OP_CODE_READ_ERROR_KEY = 0x05; private static final int OP_CODE_SELECT_OBJECT_KEY = 0x06; private static final int OP_CODE_RESPONSE_CODE_KEY = 0x60; private static final byte[] OP_CODE_CREATE_COMMAND = new byte[]{OP_CODE_CREATE_KEY, OBJECT_COMMAND, 0x00, 0x00, 0x00, 0x00 }; @@ -67,7 +67,6 @@ import no.nordicsemi.android.error.SecureDfuError; private static final byte[] OP_CODE_PACKET_RECEIPT_NOTIF_REQ = new byte[]{OP_CODE_PACKET_RECEIPT_NOTIF_REQ_KEY, 0x00, 0x00 /* param PRN uint16 in Little Endian */}; private static final byte[] OP_CODE_CALCULATE_CHECKSUM = new byte[]{OP_CODE_CALCULATE_CHECKSUM_KEY}; private static final byte[] OP_CODE_EXECUTE = new byte[]{OP_CODE_EXECUTE_KEY}; - private static final byte[] OP_CODE_READ_ERROR = new byte[]{OP_CODE_READ_ERROR_KEY}; private static final byte[] OP_CODE_SELECT_OBJECT = new byte[]{OP_CODE_SELECT_OBJECT_KEY, 0x00 /* type */}; private BluetoothGattCharacteristic mControlPointCharacteristic; @@ -101,9 +100,8 @@ import no.nordicsemi.android.error.SecureDfuError; } default: { /* - * If the DFU target device is in invalid state (f.e. the Init Packet is required but has not been selected), the target will send DFU_STATUS_INVALID_STATE error + * If the DFU target device is in invalid state (e.g. the Init Packet is required but has not been selected), the target will send DFU_STATUS_INVALID_STATE error * for each firmware packet that was send. We are interested may ignore all but the first one. - * After obtaining a remote DFU error the OP_CODE_RESET_KEY will be sent. */ if (mRemoteErrorOccurred) break; @@ -221,20 +219,10 @@ import no.nordicsemi.android.error.SecureDfuError; 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.getErrorNumber() == SecureDfuError.EXTENDED_ERROR) { - try { - final ErrorMessage details = readExtendedError(); - if (details != null) { - logi("Error details: " + details.message + " (Code = " + details.code + ")"); - mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, "Details: " + details.message + " (Code = " + details.code + ")"); - } else { - logi("Reading error details not supported"); - mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_WARNING, "Reading error details not supported"); - } - } catch (final Exception e1) { - loge("Reading error details failed", e1); - mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_WARNING, "Reading error details failed"); - } + if (e instanceof RemoteDfuExtendedErrorException) { + final RemoteDfuExtendedErrorException ee = (RemoteDfuExtendedErrorException) e; + logi("Extended Error details: " + SecureDfuError.parseExtendedError(ee.getExtendedErrorNumber())); + mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, "Details: " + SecureDfuError.parseExtendedError(ee.getExtendedErrorNumber()) + " (Code = " + ee.getExtendedErrorNumber() + ")"); } mService.terminateConnection(gatt, error); } @@ -631,6 +619,8 @@ import no.nordicsemi.android.error.SecureDfuError; // Read response final byte[] response = readNotificationResponse(); final int status = getStatusCode(response, OP_CODE_PACKET_RECEIPT_NOTIF_REQ_KEY); + if (status == SecureDfuError.EXTENDED_ERROR) + throw new RemoteDfuExtendedErrorException("Sending the number of packets failed", response[3]); if (status != DFU_STATUS_SUCCESS) throw new RemoteDfuException("Sending the number of packets failed", status); } @@ -671,58 +661,12 @@ import no.nordicsemi.android.error.SecureDfuError; final byte[] response = readNotificationResponse(); final int status = getStatusCode(response, OP_CODE_CREATE_KEY); + if (status == SecureDfuError.EXTENDED_ERROR) + throw new RemoteDfuExtendedErrorException("Creating Command object failed", response[3]); if (status != DFU_STATUS_SUCCESS) throw new RemoteDfuException("Creating Command object failed", status); } - /** - * Reads the last error message from the device. This can be executed after a {@link SecureDfuError#EXTENDED_ERROR} status is received - * from any Execute operation. - * - * @return the error details or null if this feature is not supported - * @throws DeviceDisconnectedException - * @throws DfuException - * @throws UploadAbortedException - * @throws RemoteDfuException thrown when the returned status code is not equal to {@link #DFU_STATUS_SUCCESS} - */ - private ErrorMessage readExtendedError() throws DeviceDisconnectedException, DfuException, UploadAbortedException, RemoteDfuException, UnknownResponseException { - if (!mConnected) - throw new DeviceDisconnectedException("Unable to read object info: device disconnected"); - - mRemoteErrorOccurred = false; - final BluetoothGattCharacteristic characteristic = mControlPointCharacteristic; - writeOpCode(characteristic, OP_CODE_READ_ERROR); - - final byte[] response = readNotificationResponse(); - final int status = getStatusCode(response, OP_CODE_READ_ERROR_KEY); - - if (status == DFU_STATUS_SUCCESS) { - final int code = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 3); - int length = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 5); - if (code == 0 && length == 0) - return null; - - final StringBuilder builder = new StringBuilder(); - builder.append(characteristic.getStringValue(7)); - - while (length - builder.length() > 0) { - writeOpCode(characteristic, OP_CODE_READ_ERROR); - readNotificationResponse(); - builder.append(characteristic.getStringValue(3)); - - // Finish if byte 0 is received at the end - if (characteristic.getValue()[characteristic.getValue().length - 1] == 0) - break; - } - - final ErrorMessage error = new ErrorMessage(); - error.code = code; - error.message = builder.toString(); - return error; - } - return null; - } - /** * Selects the current object and reads its metadata. The object info contains the max object size, and the offset and CRC32 of the whole object until now. * @@ -741,6 +685,8 @@ import no.nordicsemi.android.error.SecureDfuError; final byte[] response = readNotificationResponse(); final int status = getStatusCode(response, OP_CODE_SELECT_OBJECT_KEY); + if (status == SecureDfuError.EXTENDED_ERROR) + throw new RemoteDfuExtendedErrorException("Selecting object failed", response[3]); if (status != DFU_STATUS_SUCCESS) throw new RemoteDfuException("Selecting object failed", status); @@ -768,6 +714,8 @@ import no.nordicsemi.android.error.SecureDfuError; final byte[] response = readNotificationResponse(); final int status = getStatusCode(response, OP_CODE_CALCULATE_CHECKSUM_KEY); + if (status == SecureDfuError.EXTENDED_ERROR) + throw new RemoteDfuExtendedErrorException("Receiving Checksum failed", response[3]); if (status != DFU_STATUS_SUCCESS) throw new RemoteDfuException("Receiving Checksum failed", status); @@ -796,6 +744,8 @@ import no.nordicsemi.android.error.SecureDfuError; final byte[] response = readNotificationResponse(); final int status = getStatusCode(response, OP_CODE_EXECUTE_KEY); + if (status == SecureDfuError.EXTENDED_ERROR) + throw new RemoteDfuExtendedErrorException("Executing object failed", response[3]); if (status != DFU_STATUS_SUCCESS) throw new RemoteDfuException("Executing object failed", status); } @@ -808,9 +758,4 @@ import no.nordicsemi.android.error.SecureDfuError; protected int offset; protected int CRC32; } - - private class ErrorMessage { - protected int code; - protected String message; - } } 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 55ca98a..d0cb9f6 100644 --- a/dfu/src/main/java/no/nordicsemi/android/error/LegacyDfuError.java +++ b/dfu/src/main/java/no/nordicsemi/android/error/LegacyDfuError.java @@ -35,18 +35,12 @@ public final class LegacyDfuError { 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 + ")"; + 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 + ")"; } } } 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 7a8cee4..5b60912 100644 --- a/dfu/src/main/java/no/nordicsemi/android/error/SecureDfuError.java +++ b/dfu/src/main/java/no/nordicsemi/android/error/SecureDfuError.java @@ -36,27 +36,49 @@ public final class SecureDfuError { public static final int OPERATION_FAILED = 10; // 0xA public static final int EXTENDED_ERROR = 11; // 0xB + // public static final int EXT_ERROR_NO_ERROR = 0x00; // that's not an error + public static final int EXT_ERROR_WRONG_COMMAND_FORMAT = 0x02; + public static final int EXT_ERROR_UNKNOWN_COMMAND = 0x03; + public static final int EXT_ERROR_INIT_COMMAND_INVALID = 0x04; + public static final int EXT_ERROR_FW_VERSION_FAILURE = 0x05; + public static final int EXT_ERROR_HW_VERSION_FAILURE = 0x06; + public static final int EXT_ERROR_SD_VERSION_FAILURE = 0x07; + public static final int EXT_ERROR_SIGNATURE_MISSING = 0x08; + public static final int EXT_ERROR_WRONG_HASH_TYPE = 0x09; + public static final int EXT_ERROR_HASH_FAILED = 0x0A; + public static final int EXT_ERROR_WRONG_SIGNATURE_TYPE = 0x0B; + public static final int EXT_ERROR_VERIFICATION_FAILED = 0x0C; + public static final int EXT_ERROR_INSUFFICIENT_SPACE = 0x0D; + 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: - // The error details can be read using Read Error operation - return "REMOTE DFU EXTENDED ERROR"; - default: - return "UNKNOWN (" + error + ")"; + 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 + ")"; + } + } + + 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"; } } }