Splitting implementation into 2 methods
This commit is contained in:
parent
258a6ade64
commit
8b8932fd57
|
@ -207,280 +207,8 @@ import no.nordicsemi.android.error.SecureDfuError;
|
||||||
// End
|
// End
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final CRC32 crc32 = new CRC32(); // Used to calculate CRC32 of the Init packet
|
sendInitPacket(gatt);
|
||||||
ObjectChecksum checksum;
|
sendFirmware(gatt);
|
||||||
ObjectInfo info;
|
|
||||||
byte[] response;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
// First, read the Command Object Info. This give information about the maximum command size and whether there is already
|
|
||||||
// one saved from a previous connection. It offset and CRC returned are not equal zero, we can compare it with the current init file
|
|
||||||
// and skip sending it for the second time.
|
|
||||||
logi("Sending Read Command Object Info command (Op Code = 6, Type = 1)");
|
|
||||||
info = readObjectInfo(OBJECT_COMMAND);
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Command object info received (Max size = %d, Offset = %d, CRC = %08X)", info.maxSize, info.offset, info.CRC32));
|
|
||||||
if (mInitPacketSizeInBytes > info.maxSize) {
|
|
||||||
// Ignore this. DFU target will send an error if init packet is too large after sending the 'Create object' command
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can we resume? If the offset obtained from the device is greater then zero we can compare it with the local init packet CRC
|
|
||||||
// and resume sending the init packet, or even skip sending it if the whole file was sent before.
|
|
||||||
boolean skipSendingInitPacket = false;
|
|
||||||
boolean resumeSendingInitPacket = false;
|
|
||||||
if (info.offset > 0) {
|
|
||||||
try {
|
|
||||||
// Read the same number of bytes from the current init packet to calculate local CRC32
|
|
||||||
final byte[] buffer = new byte[info.offset];
|
|
||||||
mInitPacketStream.read(buffer);
|
|
||||||
// Calculate the CRC32
|
|
||||||
crc32.update(buffer);
|
|
||||||
final int crc = (int) (crc32.getValue() & 0xFFFFFFFFL);
|
|
||||||
|
|
||||||
if (info.CRC32 == crc) {
|
|
||||||
logi("Init packet CRC is the same");
|
|
||||||
if (info.offset == mInitPacketSizeInBytes) {
|
|
||||||
// The whole init packet was sent and it is equal to one we try to send now.
|
|
||||||
// There is no need to send it again. We may try to resume sending data.
|
|
||||||
logi("-> Whole Init packet was sent before");
|
|
||||||
skipSendingInitPacket = true;
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Command object match Init packet");
|
|
||||||
} else {
|
|
||||||
logi("-> " + info.offset + " bytes of Init packet were sent before");
|
|
||||||
resumeSendingInitPacket = true;
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Resuming sending Init packet...");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// A different Init packet was sent before, or the error occurred while sending.
|
|
||||||
// We have to send the while Init packet again.
|
|
||||||
mInitPacketStream.reset();
|
|
||||||
crc32.reset();
|
|
||||||
}
|
|
||||||
} catch (final IOException e) {
|
|
||||||
loge("Error while reading " + info.offset + " bytes from the init packet stream", e);
|
|
||||||
try {
|
|
||||||
// Go back to the beginning of the stream, we will send the whole init packet
|
|
||||||
mInitPacketStream.reset();
|
|
||||||
crc32.reset();
|
|
||||||
} catch (final IOException e1) {
|
|
||||||
loge("Error while resetting the init packet stream", e1);
|
|
||||||
mService.terminateConnection(gatt, DfuBaseService.ERROR_FILE_IO_EXCEPTION);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!skipSendingInitPacket) {
|
|
||||||
// The Init packet is sent different way in this implementation than the firmware, and receiving PRNs is not implemented.
|
|
||||||
// This value might have been stored on the device, so we have to explicitly disable PRNs.
|
|
||||||
logi("Disabling Packet Receipt Notifications (Op Code = 2, Value = 0)");
|
|
||||||
setPacketReceiptNotifications(0);
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Packet Receipt Notif disabled (Op Code = 2, Value = 0)");
|
|
||||||
|
|
||||||
for (int attempt = 1; attempt <= MAX_ATTEMPTS;) {
|
|
||||||
if (!resumeSendingInitPacket) {
|
|
||||||
// Create the Init object
|
|
||||||
logi("Creating Init packet object (Op Code = 1, Type = 1, Size = " + mInitPacketSizeInBytes + ")");
|
|
||||||
writeCreateRequest(OBJECT_COMMAND, mInitPacketSizeInBytes);
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Command object created");
|
|
||||||
}
|
|
||||||
// Write Init data to the Packet Characteristic
|
|
||||||
logi("Sending " + (mInitPacketSizeInBytes - info.offset) + " bytes of init packet...");
|
|
||||||
writeInitData(mPacketCharacteristic, crc32);
|
|
||||||
final int crc = (int) (crc32.getValue() & 0xFFFFFFFFL);
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Command object sent (CRC = %08X)", crc));
|
|
||||||
|
|
||||||
// Calculate Checksum
|
|
||||||
logi("Sending Calculate Checksum command (Op Code = 3)");
|
|
||||||
checksum = readChecksum();
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
|
||||||
logi(String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
|
||||||
|
|
||||||
if (crc == checksum.CRC32) {
|
|
||||||
// Everything is OK, we can proceed
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if (attempt < MAX_ATTEMPTS) {
|
|
||||||
attempt++;
|
|
||||||
logi("CRC32 does not match! Retrying...(" + attempt + "/" + MAX_ATTEMPTS + ")");
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_WARNING, "CRC32 does not match! Retrying...(" + attempt + "/" + MAX_ATTEMPTS + ")");
|
|
||||||
try {
|
|
||||||
// Go back to the beginning, we will send the whole Init packet again
|
|
||||||
resumeSendingInitPacket = false;
|
|
||||||
info.offset = 0;
|
|
||||||
info.CRC32 = 0;
|
|
||||||
mInitPacketStream.reset();
|
|
||||||
crc32.reset();
|
|
||||||
} catch (final IOException e) {
|
|
||||||
loge("Error while resetting the init packet stream", e);
|
|
||||||
mService.terminateConnection(gatt, DfuBaseService.ERROR_FILE_IO_EXCEPTION);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loge("CRC32 does not match!");
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, "CRC32 does not match!");
|
|
||||||
mService.terminateConnection(gatt, DfuBaseService.ERROR_CRC_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute Init packet
|
|
||||||
logi("Executing init packet (Op Code = 4)");
|
|
||||||
writeExecute();
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Command object executed");
|
|
||||||
} else {
|
|
||||||
// When the Init file was skipped, we are still not sure whether it was executed.
|
|
||||||
// Perhaps, just after sending it, the device got disconnected? We have to check it.
|
|
||||||
logi("Checking the current object...");
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Checking the current object...");
|
|
||||||
|
|
||||||
// Get the calculated CRC of the Init packet
|
|
||||||
final int crc = (int) (crc32.getValue() & 0xFFFFFFFFL);
|
|
||||||
|
|
||||||
// Calculate Checksum
|
|
||||||
logi("Sending Calculate Checksum command (Op Code = 3)");
|
|
||||||
checksum = readChecksum();
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
|
||||||
logi(String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
|
||||||
|
|
||||||
if (checksum.offset == mInitPacketSizeInBytes && crc == checksum.CRC32) {
|
|
||||||
// Execute Init packet. It's better to execute it twice than not execute at all...
|
|
||||||
logi("Executing init packet (Op Code = 4)");
|
|
||||||
writeExecute();
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Command object executed");
|
|
||||||
} else {
|
|
||||||
// It looks like we have started sending the Data already, let's continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the number of packets of firmware before receiving a receipt notification
|
|
||||||
final int numberOfPacketsBeforeNotification = mPacketsBeforeNotification;
|
|
||||||
if (numberOfPacketsBeforeNotification > 0) {
|
|
||||||
logi("Sending the number of packets before notifications (Op Code = 2, Value = " + numberOfPacketsBeforeNotification + ")");
|
|
||||||
setPacketReceiptNotifications(numberOfPacketsBeforeNotification);
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Packet Receipt Notif Req (Op Code = 2) sent (Value = " + numberOfPacketsBeforeNotification + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are ready to start sending the new firmware.
|
|
||||||
// Before, we have to check the max object size and i
|
|
||||||
|
|
||||||
logi("Sending Read Data Object Info command (Op Code = 6, Type = 2)");
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Reading data object info...");
|
|
||||||
info = readObjectInfo(OBJECT_DATA);
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Data object info received (Max size = %d, Offset = %d, CRC = %08X)", info.maxSize, info.offset, info.CRC32));
|
|
||||||
mProgressInfo.setMaxObjectSizeInBytes(info.maxSize);
|
|
||||||
|
|
||||||
// Number of chunks in which the data will be sent
|
|
||||||
final int count = (mImageSizeInBytes + info.maxSize - 1) / info.maxSize;
|
|
||||||
// Chunk iterator
|
|
||||||
int i = 0;
|
|
||||||
final long startTime = SystemClock.elapsedRealtime();
|
|
||||||
boolean resumeSendingData = false;
|
|
||||||
|
|
||||||
// Can we resume? If the offset obtained from the device is greater then zero we can compare it with the local CRC
|
|
||||||
// and resume sending the data.
|
|
||||||
if (info.offset > 0) {
|
|
||||||
try {
|
|
||||||
i = info.offset / info.maxSize;
|
|
||||||
final int bytesSentAndExecuted = info.maxSize * i;
|
|
||||||
final int bytesSentNotExecuted = info.offset - bytesSentAndExecuted;
|
|
||||||
|
|
||||||
// Read the same number of bytes from the current init packet to calculate local CRC32
|
|
||||||
if (bytesSentAndExecuted > 0) {
|
|
||||||
mFirmwareStream.read(new byte[bytesSentAndExecuted]); // Read executed bytes
|
|
||||||
mFirmwareStream.mark(info.maxSize); // Mark here
|
|
||||||
}
|
|
||||||
mFirmwareStream.read(new byte[bytesSentNotExecuted]); // Read the rest
|
|
||||||
|
|
||||||
// Calculate the CRC32
|
|
||||||
final int crc = (int) (((ArchiveInputStream) mFirmwareStream).getCrc32() & 0xFFFFFFFFL);
|
|
||||||
|
|
||||||
if (crc == info.CRC32) {
|
|
||||||
mProgressInfo.setBytesSent(info.offset);
|
|
||||||
mProgressInfo.setBytesReceived(info.offset);
|
|
||||||
resumeSendingData = true;
|
|
||||||
} else {
|
|
||||||
// The CRC of the current object is not correct. If there was another Data object sent before, its CRC must have been correct,
|
|
||||||
// as it has been executed. Either way, we have to create the current object again.
|
|
||||||
mProgressInfo.setBytesSent(bytesSentAndExecuted);
|
|
||||||
mProgressInfo.setBytesReceived(bytesSentAndExecuted);
|
|
||||||
mFirmwareStream.reset();
|
|
||||||
}
|
|
||||||
} catch (final IOException e) {
|
|
||||||
loge("Error while reading firmware stream", e);
|
|
||||||
mService.terminateConnection(gatt, DfuBaseService.ERROR_FILE_IO_EXCEPTION);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Initialize the timer used to calculate the transfer speed
|
|
||||||
mProgressInfo.setBytesSent(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Each page will be sent in MAX_ATTEMPTS
|
|
||||||
int attempt = 1;
|
|
||||||
while (mProgressInfo.getAvailableObjectSizeIsBytes() > 0) {
|
|
||||||
if (!resumeSendingData) {
|
|
||||||
// Create the Data object
|
|
||||||
logi("Creating Data object (Op Code = 1, Type = 2, Size = " + mProgressInfo.getAvailableObjectSizeIsBytes() + ") (" + (i + 1) + "/" + count + ")");
|
|
||||||
writeCreateRequest(OBJECT_DATA, mProgressInfo.getAvailableObjectSizeIsBytes());
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Data object (" + (i + 1) + "/" + count + ") created");
|
|
||||||
|
|
||||||
resumeSendingData = false;
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Uploading firmware...");
|
|
||||||
} else {
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Resuming uploading firmware...");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the current object part
|
|
||||||
try {
|
|
||||||
logi("Uploading firmware...");
|
|
||||||
uploadFirmwareImage(mPacketCharacteristic);
|
|
||||||
} catch (final DeviceDisconnectedException e) {
|
|
||||||
loge("Disconnected while sending data");
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate Checksum
|
|
||||||
logi("Sending Calculate Checksum command (Op Code = 3)");
|
|
||||||
checksum = readChecksum();
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
|
||||||
logi(String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
|
||||||
|
|
||||||
// Calculate the CRC32
|
|
||||||
final int crc = (int) (((ArchiveInputStream) mFirmwareStream).getCrc32() & 0xFFFFFFFFL);
|
|
||||||
if (crc == checksum.CRC32) {
|
|
||||||
// Execute Init packet
|
|
||||||
logi("Executing data object (Op Code = 4)");
|
|
||||||
writeExecute();
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Data object executed");
|
|
||||||
|
|
||||||
// Increment iterator
|
|
||||||
i++;
|
|
||||||
attempt = 1;
|
|
||||||
} else {
|
|
||||||
if (attempt < MAX_ATTEMPTS) {
|
|
||||||
attempt++;
|
|
||||||
logi("CRC32 does not match! Retrying...(" + attempt + "/" + MAX_ATTEMPTS + ")");
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_WARNING, "CRC32 does not match! Retrying...(" + attempt + "/" + MAX_ATTEMPTS + ")");
|
|
||||||
try {
|
|
||||||
mFirmwareStream.reset();
|
|
||||||
mProgressInfo.setBytesSent(checksum.offset - info.maxSize);
|
|
||||||
} catch (final IOException e) {
|
|
||||||
loge("Error while resetting the firmware stream", e);
|
|
||||||
mService.terminateConnection(gatt, DfuBaseService.ERROR_FILE_IO_EXCEPTION);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loge("CRC32 does not match!");
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, "CRC32 does not match!");
|
|
||||||
mService.terminateConnection(gatt, DfuBaseService.ERROR_CRC_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final long endTime = SystemClock.elapsedRealtime();
|
|
||||||
logi("Transfer of " + mProgressInfo.getBytesSent() + " bytes has taken " + (endTime - startTime) + " ms");
|
|
||||||
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Upload completed in " + (endTime - startTime) + " ms");
|
|
||||||
|
|
||||||
// The device will reset so we don't have to send Disconnect signal.
|
// The device will reset so we don't have to send Disconnect signal.
|
||||||
mProgressInfo.setProgress(DfuBaseService.PROGRESS_DISCONNECTING);
|
mProgressInfo.setProgress(DfuBaseService.PROGRESS_DISCONNECTING);
|
||||||
|
@ -511,6 +239,319 @@ import no.nordicsemi.android.error.SecureDfuError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method does the following:
|
||||||
|
* <ol>
|
||||||
|
* <li>Reads Command Object Info - this returns the maximum acceptable size of a command object, and the offset and CRC32 of the command
|
||||||
|
* that is already stored in the device (in case the DFU was started in a previous connection and disconnected before it has finished).</li>
|
||||||
|
* <li>If the offset received is greater than 0 and less or equal to the size of the Init file that is to be sent, it will compare the
|
||||||
|
* received CRC with the local one and, if they match:
|
||||||
|
* <ul>
|
||||||
|
* <li>If offset < init file size - it will continue sending the Init file from the point it stopped before,</li>
|
||||||
|
* <li>If offset == init file size - it will send the Calculate Checksum request to obtain a CRC of a current object.
|
||||||
|
* <ul>
|
||||||
|
* <li>If the received CRC is the same as above ones, it will send Execute command to execute the Init file, as it may have not been executed before.</li>
|
||||||
|
* <li>If the received CRC is different, that means that the Data Object has already been created in the previous connection and this method will return.</li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* <li>If the CRCs don't match, or the received offset is > init file size, it create the Command Object and send the whole Init file as the previous one
|
||||||
|
* was different.</li>
|
||||||
|
* </ol>
|
||||||
|
* Sending of the Init packet is done without using PRNs (Packet Receipt Notifications), so they are disabled prior to sending the data.
|
||||||
|
* @param gatt the target GATT device
|
||||||
|
* @throws RemoteDfuException
|
||||||
|
* @throws DeviceDisconnectedException
|
||||||
|
* @throws DfuException
|
||||||
|
* @throws UploadAbortedException
|
||||||
|
* @throws UnknownResponseException
|
||||||
|
*/
|
||||||
|
private void sendInitPacket(final BluetoothGatt gatt) throws RemoteDfuException, DeviceDisconnectedException, DfuException, UploadAbortedException, UnknownResponseException {
|
||||||
|
final CRC32 crc32 = new CRC32(); // Used to calculate CRC32 of the Init packet
|
||||||
|
ObjectChecksum checksum;
|
||||||
|
|
||||||
|
// First, read the Command Object Info. This give information about the maximum command size and whether there is already
|
||||||
|
// one saved from a previous connection. It offset and CRC returned are not equal zero, we can compare it with the current init file
|
||||||
|
// and skip sending it for the second time.
|
||||||
|
logi("Sending Read Command Object Info command (Op Code = 6, Type = 1)");
|
||||||
|
final ObjectInfo info = readObjectInfo(OBJECT_COMMAND);
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Command object info received (Max size = %d, Offset = %d, CRC = %08X)", info.maxSize, info.offset, info.CRC32));
|
||||||
|
if (mInitPacketSizeInBytes > info.maxSize) {
|
||||||
|
// Ignore this. DFU target will send an error if init packet is too large after sending the 'Create object' command
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can we resume? If the offset obtained from the device is greater then zero we can compare it with the local init packet CRC
|
||||||
|
// and resume sending the init packet, or even skip sending it if the whole file was sent before.
|
||||||
|
boolean skipSendingInitPacket = false;
|
||||||
|
boolean resumeSendingInitPacket = false;
|
||||||
|
if (info.offset > 0 && info.offset <= mInitPacketSizeInBytes) {
|
||||||
|
try {
|
||||||
|
// Read the same number of bytes from the current init packet to calculate local CRC32
|
||||||
|
final byte[] buffer = new byte[info.offset];
|
||||||
|
mInitPacketStream.read(buffer);
|
||||||
|
// Calculate the CRC32
|
||||||
|
crc32.update(buffer);
|
||||||
|
final int crc = (int) (crc32.getValue() & 0xFFFFFFFFL);
|
||||||
|
|
||||||
|
if (info.CRC32 == crc) {
|
||||||
|
logi("Init packet CRC is the same");
|
||||||
|
if (info.offset == mInitPacketSizeInBytes) {
|
||||||
|
// The whole init packet was sent and it is equal to one we try to send now.
|
||||||
|
// There is no need to send it again. We may try to resume sending data.
|
||||||
|
logi("-> Whole Init packet was sent before");
|
||||||
|
skipSendingInitPacket = true;
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Command object match Init packet");
|
||||||
|
} else {
|
||||||
|
logi("-> " + info.offset + " bytes of Init packet were sent before");
|
||||||
|
resumeSendingInitPacket = true;
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Resuming sending Init packet...");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// A different Init packet was sent before, or the error occurred while sending.
|
||||||
|
// We have to send the whole Init packet again.
|
||||||
|
mInitPacketStream.reset();
|
||||||
|
crc32.reset();
|
||||||
|
}
|
||||||
|
} catch (final IOException e) {
|
||||||
|
loge("Error while reading " + info.offset + " bytes from the init packet stream", e);
|
||||||
|
try {
|
||||||
|
// Go back to the beginning of the stream, we will send the whole init packet
|
||||||
|
mInitPacketStream.reset();
|
||||||
|
crc32.reset();
|
||||||
|
} catch (final IOException e1) {
|
||||||
|
loge("Error while resetting the init packet stream", e1);
|
||||||
|
mService.terminateConnection(gatt, DfuBaseService.ERROR_FILE_IO_EXCEPTION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skipSendingInitPacket) {
|
||||||
|
// The Init packet is sent different way in this implementation than the firmware, and receiving PRNs is not implemented.
|
||||||
|
// This value might have been stored on the device, so we have to explicitly disable PRNs.
|
||||||
|
logi("Disabling Packet Receipt Notifications (Op Code = 2, Value = 0)");
|
||||||
|
setPacketReceiptNotifications(0);
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Packet Receipt Notif disabled (Op Code = 2, Value = 0)");
|
||||||
|
|
||||||
|
for (int attempt = 1; attempt <= MAX_ATTEMPTS;) {
|
||||||
|
if (!resumeSendingInitPacket) {
|
||||||
|
// Create the Init object
|
||||||
|
logi("Creating Init packet object (Op Code = 1, Type = 1, Size = " + mInitPacketSizeInBytes + ")");
|
||||||
|
writeCreateRequest(OBJECT_COMMAND, mInitPacketSizeInBytes);
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Command object created");
|
||||||
|
}
|
||||||
|
// Write Init data to the Packet Characteristic
|
||||||
|
logi("Sending " + (mInitPacketSizeInBytes - info.offset) + " bytes of init packet...");
|
||||||
|
writeInitData(mPacketCharacteristic, crc32);
|
||||||
|
final int crc = (int) (crc32.getValue() & 0xFFFFFFFFL);
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Command object sent (CRC = %08X)", crc));
|
||||||
|
|
||||||
|
// Calculate Checksum
|
||||||
|
logi("Sending Calculate Checksum command (Op Code = 3)");
|
||||||
|
checksum = readChecksum();
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
||||||
|
logi(String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
||||||
|
|
||||||
|
if (crc == checksum.CRC32) {
|
||||||
|
// Everything is OK, we can proceed
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (attempt < MAX_ATTEMPTS) {
|
||||||
|
attempt++;
|
||||||
|
logi("CRC32 does not match! Retrying...(" + attempt + "/" + MAX_ATTEMPTS + ")");
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_WARNING, "CRC32 does not match! Retrying...(" + attempt + "/" + MAX_ATTEMPTS + ")");
|
||||||
|
try {
|
||||||
|
// Go back to the beginning, we will send the whole Init packet again
|
||||||
|
resumeSendingInitPacket = false;
|
||||||
|
info.offset = 0;
|
||||||
|
info.CRC32 = 0;
|
||||||
|
mInitPacketStream.reset();
|
||||||
|
crc32.reset();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
loge("Error while resetting the init packet stream", e);
|
||||||
|
mService.terminateConnection(gatt, DfuBaseService.ERROR_FILE_IO_EXCEPTION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loge("CRC32 does not match!");
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, "CRC32 does not match!");
|
||||||
|
mService.terminateConnection(gatt, DfuBaseService.ERROR_CRC_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute Init packet
|
||||||
|
logi("Executing init packet (Op Code = 4)");
|
||||||
|
writeExecute();
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Command object executed");
|
||||||
|
} else {
|
||||||
|
// When the Init file was skipped, we are still not sure whether it was executed.
|
||||||
|
// Perhaps, just after sending it, the device got disconnected? We have to check it.
|
||||||
|
logi("Checking the current object...");
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Checking the current object...");
|
||||||
|
|
||||||
|
// Get the calculated CRC of the Init packet
|
||||||
|
final int crc = (int) (crc32.getValue() & 0xFFFFFFFFL);
|
||||||
|
|
||||||
|
// Calculate Checksum
|
||||||
|
logi("Sending Calculate Checksum command (Op Code = 3)");
|
||||||
|
checksum = readChecksum();
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
||||||
|
logi(String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
||||||
|
|
||||||
|
if (checksum.offset == mInitPacketSizeInBytes && crc == checksum.CRC32) {
|
||||||
|
// Execute Init packet. It's better to execute it twice than not execute at all...
|
||||||
|
logi("Executing init packet (Op Code = 4)");
|
||||||
|
writeExecute();
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Command object executed");
|
||||||
|
} else {
|
||||||
|
// It looks like we have started sending the Data already, let's continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method enables
|
||||||
|
* @param gatt the target GATT device
|
||||||
|
* @throws RemoteDfuException
|
||||||
|
* @throws DeviceDisconnectedException
|
||||||
|
* @throws DfuException
|
||||||
|
* @throws UploadAbortedException
|
||||||
|
* @throws UnknownResponseException
|
||||||
|
*/
|
||||||
|
private void sendFirmware(final BluetoothGatt gatt) throws RemoteDfuException, DeviceDisconnectedException, DfuException, UploadAbortedException, UnknownResponseException {
|
||||||
|
// Send the number of packets of firmware before receiving a receipt notification
|
||||||
|
final int numberOfPacketsBeforeNotification = mPacketsBeforeNotification;
|
||||||
|
if (numberOfPacketsBeforeNotification > 0) {
|
||||||
|
logi("Sending the number of packets before notifications (Op Code = 2, Value = " + numberOfPacketsBeforeNotification + ")");
|
||||||
|
setPacketReceiptNotifications(numberOfPacketsBeforeNotification);
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Packet Receipt Notif Req (Op Code = 2) sent (Value = " + numberOfPacketsBeforeNotification + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are ready to start sending the new firmware.
|
||||||
|
// Before, we have to check the max object size and i
|
||||||
|
|
||||||
|
logi("Sending Read Data Object Info command (Op Code = 6, Type = 2)");
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Reading data object info...");
|
||||||
|
final ObjectInfo info = readObjectInfo(OBJECT_DATA);
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Data object info received (Max size = %d, Offset = %d, CRC = %08X)", info.maxSize, info.offset, info.CRC32));
|
||||||
|
mProgressInfo.setMaxObjectSizeInBytes(info.maxSize);
|
||||||
|
|
||||||
|
// Number of chunks in which the data will be sent
|
||||||
|
final int count = (mImageSizeInBytes + info.maxSize - 1) / info.maxSize;
|
||||||
|
// Chunk iterator
|
||||||
|
int i = 0;
|
||||||
|
final long startTime = SystemClock.elapsedRealtime();
|
||||||
|
boolean resumeSendingData = false;
|
||||||
|
|
||||||
|
// Can we resume? If the offset obtained from the device is greater then zero we can compare it with the local CRC
|
||||||
|
// and resume sending the data.
|
||||||
|
if (info.offset > 0) {
|
||||||
|
try {
|
||||||
|
i = info.offset / info.maxSize;
|
||||||
|
final int bytesSentAndExecuted = info.maxSize * i;
|
||||||
|
final int bytesSentNotExecuted = info.offset - bytesSentAndExecuted;
|
||||||
|
|
||||||
|
// Read the same number of bytes from the current init packet to calculate local CRC32
|
||||||
|
if (bytesSentAndExecuted > 0) {
|
||||||
|
mFirmwareStream.read(new byte[bytesSentAndExecuted]); // Read executed bytes
|
||||||
|
mFirmwareStream.mark(info.maxSize); // Mark here
|
||||||
|
}
|
||||||
|
mFirmwareStream.read(new byte[bytesSentNotExecuted]); // Read the rest
|
||||||
|
|
||||||
|
// Calculate the CRC32
|
||||||
|
final int crc = (int) (((ArchiveInputStream) mFirmwareStream).getCrc32() & 0xFFFFFFFFL);
|
||||||
|
|
||||||
|
if (crc == info.CRC32) {
|
||||||
|
mProgressInfo.setBytesSent(info.offset);
|
||||||
|
mProgressInfo.setBytesReceived(info.offset);
|
||||||
|
resumeSendingData = true;
|
||||||
|
} else {
|
||||||
|
// The CRC of the current object is not correct. If there was another Data object sent before, its CRC must have been correct,
|
||||||
|
// as it has been executed. Either way, we have to create the current object again.
|
||||||
|
mProgressInfo.setBytesSent(bytesSentAndExecuted);
|
||||||
|
mProgressInfo.setBytesReceived(bytesSentAndExecuted);
|
||||||
|
mFirmwareStream.reset();
|
||||||
|
}
|
||||||
|
} catch (final IOException e) {
|
||||||
|
loge("Error while reading firmware stream", e);
|
||||||
|
mService.terminateConnection(gatt, DfuBaseService.ERROR_FILE_IO_EXCEPTION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Initialize the timer used to calculate the transfer speed
|
||||||
|
mProgressInfo.setBytesSent(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each page will be sent in MAX_ATTEMPTS
|
||||||
|
int attempt = 1;
|
||||||
|
while (mProgressInfo.getAvailableObjectSizeIsBytes() > 0) {
|
||||||
|
if (!resumeSendingData) {
|
||||||
|
// Create the Data object
|
||||||
|
logi("Creating Data object (Op Code = 1, Type = 2, Size = " + mProgressInfo.getAvailableObjectSizeIsBytes() + ") (" + (i + 1) + "/" + count + ")");
|
||||||
|
writeCreateRequest(OBJECT_DATA, mProgressInfo.getAvailableObjectSizeIsBytes());
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Data object (" + (i + 1) + "/" + count + ") created");
|
||||||
|
|
||||||
|
resumeSendingData = false;
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Uploading firmware...");
|
||||||
|
} else {
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Resuming uploading firmware...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the current object part
|
||||||
|
try {
|
||||||
|
logi("Uploading firmware...");
|
||||||
|
uploadFirmwareImage(mPacketCharacteristic);
|
||||||
|
} catch (final DeviceDisconnectedException e) {
|
||||||
|
loge("Disconnected while sending data");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate Checksum
|
||||||
|
logi("Sending Calculate Checksum command (Op Code = 3)");
|
||||||
|
final ObjectChecksum checksum = readChecksum();
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
||||||
|
logi(String.format(Locale.US, "Checksum received (Offset = %d, CRC = %08X)", checksum.offset, checksum.CRC32));
|
||||||
|
|
||||||
|
// Calculate the CRC32
|
||||||
|
final int crc = (int) (((ArchiveInputStream) mFirmwareStream).getCrc32() & 0xFFFFFFFFL);
|
||||||
|
if (crc == checksum.CRC32) {
|
||||||
|
// Execute Init packet
|
||||||
|
logi("Executing data object (Op Code = 4)");
|
||||||
|
writeExecute();
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Data object executed");
|
||||||
|
|
||||||
|
// Increment iterator
|
||||||
|
i++;
|
||||||
|
attempt = 1;
|
||||||
|
} else {
|
||||||
|
if (attempt < MAX_ATTEMPTS) {
|
||||||
|
attempt++;
|
||||||
|
logi("CRC32 does not match! Retrying...(" + attempt + "/" + MAX_ATTEMPTS + ")");
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_WARNING, "CRC32 does not match! Retrying...(" + attempt + "/" + MAX_ATTEMPTS + ")");
|
||||||
|
try {
|
||||||
|
mFirmwareStream.reset();
|
||||||
|
mProgressInfo.setBytesSent(checksum.offset - info.maxSize);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
loge("Error while resetting the firmware stream", e);
|
||||||
|
mService.terminateConnection(gatt, DfuBaseService.ERROR_FILE_IO_EXCEPTION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loge("CRC32 does not match!");
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_ERROR, "CRC32 does not match!");
|
||||||
|
mService.terminateConnection(gatt, DfuBaseService.ERROR_CRC_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final long endTime = SystemClock.elapsedRealtime();
|
||||||
|
logi("Transfer of " + mProgressInfo.getBytesSent() + " bytes has taken " + (endTime - startTime) + " ms");
|
||||||
|
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Upload completed in " + (endTime - startTime) + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the response received is valid and returns the status code.
|
* Checks whether the response received is valid and returns the status code.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue