Support for extended errors added

This commit is contained in:
Aleksander Nowakowski 2016-11-23 17:56:01 +01:00
parent 300dadf124
commit 7ee6b3258a
3 changed files with 63 additions and 102 deletions

View File

@ -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.DeviceDisconnectedException;
import no.nordicsemi.android.dfu.internal.exception.DfuException; import no.nordicsemi.android.dfu.internal.exception.DfuException;
import no.nordicsemi.android.dfu.internal.exception.RemoteDfuException; 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.UnknownResponseException;
import no.nordicsemi.android.dfu.internal.exception.UploadAbortedException; import no.nordicsemi.android.dfu.internal.exception.UploadAbortedException;
import no.nordicsemi.android.error.SecureDfuError; 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_PACKET_RECEIPT_NOTIF_REQ_KEY = 0x02;
private static final int OP_CODE_CALCULATE_CHECKSUM_KEY = 0x03; 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_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_SELECT_OBJECT_KEY = 0x06;
private static final int OP_CODE_RESPONSE_CODE_KEY = 0x60; 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 }; 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_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_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_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 static final byte[] OP_CODE_SELECT_OBJECT = new byte[]{OP_CODE_SELECT_OBJECT_KEY, 0x00 /* type */};
private BluetoothGattCharacteristic mControlPointCharacteristic; private BluetoothGattCharacteristic mControlPointCharacteristic;
@ -101,9 +100,8 @@ import no.nordicsemi.android.error.SecureDfuError;
} }
default: { 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. * 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) if (mRemoteErrorOccurred)
break; 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))); 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. // For the Extended Error more details can be obtained on some devices.
if (e.getErrorNumber() == SecureDfuError.EXTENDED_ERROR) { if (e instanceof RemoteDfuExtendedErrorException) {
try { final RemoteDfuExtendedErrorException ee = (RemoteDfuExtendedErrorException) e;
final ErrorMessage details = readExtendedError(); logi("Extended Error details: " + SecureDfuError.parseExtendedError(ee.getExtendedErrorNumber()));
if (details != null) { mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, "Details: " + SecureDfuError.parseExtendedError(ee.getExtendedErrorNumber()) + " (Code = " + ee.getExtendedErrorNumber() + ")");
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");
}
} }
mService.terminateConnection(gatt, error); mService.terminateConnection(gatt, error);
} }
@ -631,6 +619,8 @@ import no.nordicsemi.android.error.SecureDfuError;
// Read response // Read response
final byte[] response = readNotificationResponse(); final byte[] response = readNotificationResponse();
final int status = getStatusCode(response, OP_CODE_PACKET_RECEIPT_NOTIF_REQ_KEY); 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) if (status != DFU_STATUS_SUCCESS)
throw new RemoteDfuException("Sending the number of packets failed", status); 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 byte[] response = readNotificationResponse();
final int status = getStatusCode(response, OP_CODE_CREATE_KEY); 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) if (status != DFU_STATUS_SUCCESS)
throw new RemoteDfuException("Creating Command object failed", status); 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. * 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 byte[] response = readNotificationResponse();
final int status = getStatusCode(response, OP_CODE_SELECT_OBJECT_KEY); 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) if (status != DFU_STATUS_SUCCESS)
throw new RemoteDfuException("Selecting object failed", status); throw new RemoteDfuException("Selecting object failed", status);
@ -768,6 +714,8 @@ import no.nordicsemi.android.error.SecureDfuError;
final byte[] response = readNotificationResponse(); final byte[] response = readNotificationResponse();
final int status = getStatusCode(response, OP_CODE_CALCULATE_CHECKSUM_KEY); 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) if (status != DFU_STATUS_SUCCESS)
throw new RemoteDfuException("Receiving Checksum failed", status); throw new RemoteDfuException("Receiving Checksum failed", status);
@ -796,6 +744,8 @@ import no.nordicsemi.android.error.SecureDfuError;
final byte[] response = readNotificationResponse(); final byte[] response = readNotificationResponse();
final int status = getStatusCode(response, OP_CODE_EXECUTE_KEY); 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) if (status != DFU_STATUS_SUCCESS)
throw new RemoteDfuException("Executing object failed", status); throw new RemoteDfuException("Executing object failed", status);
} }
@ -808,9 +758,4 @@ import no.nordicsemi.android.error.SecureDfuError;
protected int offset; protected int offset;
protected int CRC32; protected int CRC32;
} }
private class ErrorMessage {
protected int code;
protected String message;
}
} }

View File

@ -35,18 +35,12 @@ public final class LegacyDfuError {
public static String parse(final int error) { public static String parse(final int error) {
switch (error & (~DfuBaseService.ERROR_REMOTE_MASK)) { switch (error & (~DfuBaseService.ERROR_REMOTE_MASK)) {
case INVALID_STATE: case INVALID_STATE: return "REMOTE DFU INVALID STATE";
return "REMOTE DFU INVALID STATE"; case NOT_SUPPORTED: return "REMOTE DFU NOT SUPPORTED";
case NOT_SUPPORTED: case DATA_SIZE_EXCEEDS_LIMIT: return "REMOTE DFU DATA SIZE EXCEEDS LIMIT";
return "REMOTE DFU NOT SUPPORTED"; case CRC_ERROR: return "REMOTE DFU INVALID CRC ERROR";
case DATA_SIZE_EXCEEDS_LIMIT: case OPERATION_FAILED: return "REMOTE DFU OPERATION FAILED";
return "REMOTE DFU DATA SIZE EXCEEDS LIMIT"; default: return "UNKNOWN (" + error + ")";
case CRC_ERROR:
return "REMOTE DFU INVALID CRC ERROR";
case OPERATION_FAILED:
return "REMOTE DFU OPERATION FAILED";
default:
return "UNKNOWN (" + error + ")";
} }
} }
} }

View File

@ -36,27 +36,49 @@ public final class SecureDfuError {
public static final int OPERATION_FAILED = 10; // 0xA public static final int OPERATION_FAILED = 10; // 0xA
public static final int EXTENDED_ERROR = 11; // 0xB 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) { public static String parse(final int error) {
switch (error & (~DfuBaseService.ERROR_REMOTE_MASK)) { switch (error & (~DfuBaseService.ERROR_REMOTE_MASK)) {
case OP_CODE_NOT_SUPPORTED: case OP_CODE_NOT_SUPPORTED: return "REMOTE DFU OP CODE NOT SUPPORTED";
return "REMOTE DFU OP CODE NOT SUPPORTED"; case INVALID_PARAM: return "REMOTE DFU INVALID PARAM";
case INVALID_PARAM: case INSUFFICIENT_RESOURCES: return "REMOTE DFU INSUFFICIENT RESOURCES";
return "REMOTE DFU INVALID PARAM"; case INVALID_OBJECT: return "REMOTE DFU INVALID OBJECT";
case INSUFFICIENT_RESOURCES: case UNSUPPORTED_TYPE: return "REMOTE DFU UNSUPPORTED TYPE";
return "REMOTE DFU INSUFFICIENT RESOURCES"; case OPERATION_NOT_PERMITTED: return "REMOTE DFU OPERATION NOT PERMITTED";
case INVALID_OBJECT: case OPERATION_FAILED: return "REMOTE DFU OPERATION FAILED";
return "REMOTE DFU INVALID OBJECT"; case EXTENDED_ERROR: return "REMOTE DFU EXTENDED ERROR";
case UNSUPPORTED_TYPE: default: return "UNKNOWN (" + error + ")";
return "REMOTE DFU UNSUPPORTED TYPE"; }
case OPERATION_NOT_PERMITTED: }
return "REMOTE DFU OPERATION NOT PERMITTED";
case OPERATION_FAILED: public static String parseExtendedError(final int error) {
return "REMOTE DFU OPERATION FAILED"; switch (error) {
case EXTENDED_ERROR: case EXT_ERROR_WRONG_COMMAND_FORMAT: return "Wrong command format";
// The error details can be read using Read Error operation case EXT_ERROR_UNKNOWN_COMMAND: return "Unknown command";
return "REMOTE DFU EXTENDED ERROR"; case EXT_ERROR_INIT_COMMAND_INVALID: return "Init command invalid";
default: case EXT_ERROR_FW_VERSION_FAILURE: return "FW version failure";
return "UNKNOWN (" + error + ")"; 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";
} }
} }
} }