Bug fixed: #77

This commit is contained in:
Aleksander Nowakowski 2017-10-19 15:46:46 +02:00
parent a3ae736357
commit ff73f661eb
9 changed files with 96 additions and 72 deletions

View File

@ -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 + ")";
}
}
}

View File

@ -422,7 +422,7 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
/**
* The broadcast error message contains the following extras:
* <ul>
* <li>{@link #EXTRA_DATA} - the error number. Use {@link GattError#parse(int)} to get String representation</li>
* <li>{@link #EXTRA_DATA} - the error number. Use {@link GattError#parse(int)} to get String representation.</li>
* <li>{@link #EXTRA_DEVICE_ADDRESS} - the target device address</li>
* </ul>
*/
@ -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}.
*/

View File

@ -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));

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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 + ")";
}
}

View File

@ -29,6 +29,7 @@ import no.nordicsemi.android.dfu.DfuBaseService;
/**
* Parses the error numbers according to the <b>gatt_api.h</b> 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 + ")";
}
}

View File

@ -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 + ")";
}
}
}

View File

@ -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 + ")";
}
}
}