Resuming DFU completed. Legacy may nt work.

This commit is contained in:
Aleksander Nowakowski 2016-06-10 11:13:52 +02:00
parent 09cb5f76eb
commit 258a6ade64
3 changed files with 144 additions and 31 deletions

View File

@ -33,6 +33,7 @@ import android.support.annotation.NonNull;
private final ProgressListener mListener;
private int progress;
private int bytesSent;
private int initalBytesSent;
private int lastBytesSent;
private int bytesReceived;
private int imageSizeInBytes;
@ -64,8 +65,9 @@ import android.support.annotation.NonNull;
}
public void setBytesSent(final int bytesSent) {
if (this.bytesSent == 0 && bytesSent == 0) {
if (timeStart == 0) {
timeStart = SystemClock.elapsedRealtime();
initalBytesSent = bytesSent;
}
this.bytesSent = bytesSent;
this.progress = (int) (100.0f * bytesSent / imageSizeInBytes);
@ -116,7 +118,7 @@ import android.support.annotation.NonNull;
public float getSpeed() {
final long now = SystemClock.elapsedRealtime();
final float speed = now - timeStart != 0 ? (float) (bytesSent - lastBytesSent) / (float) (now - lastProgressTime) : 0.0f;
final float speed = now - timeStart != 0 ? (float) (bytesSent - lastBytesSent - initalBytesSent) / (float) (now - lastProgressTime) : 0.0f;
lastProgressTime = now;
lastBytesSent = bytesSent;
return speed;
@ -124,7 +126,7 @@ import android.support.annotation.NonNull;
public float getAverageSpeed() {
final long now = SystemClock.elapsedRealtime();
return now - timeStart != 0 ? (float) bytesSent / (float) (now - timeStart) : 0.0f;
return now - timeStart != 0 ? (float) (bytesSent - initalBytesSent) / (float) (now - timeStart) : 0.0f;
}
public int getCurrentPart() {

View File

@ -34,6 +34,7 @@ import java.util.Locale;
import java.util.UUID;
import java.util.zip.CRC32;
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;
@ -48,6 +49,7 @@ import no.nordicsemi.android.error.SecureDfuError;
protected static final UUID DFU_PACKET_UUID = new UUID(0x000015321212EFDEL, 0x1523785FEABCD123L);
private static final int DFU_STATUS_SUCCESS = 1;
private static final int MAX_ATTEMPTS = 3;
// Object types
private static final int OBJECT_COMMAND = 0x01;
@ -232,7 +234,7 @@ import no.nordicsemi.android.error.SecureDfuError;
mInitPacketStream.read(buffer);
// Calculate the CRC32
crc32.update(buffer);
final int crc = (int) (crc32.getValue() & 0xFFFFFFFL);
final int crc = (int) (crc32.getValue() & 0xFFFFFFFFL);
if (info.CRC32 == crc) {
logi("Init packet CRC is the same");
@ -274,7 +276,7 @@ import no.nordicsemi.android.error.SecureDfuError;
setPacketReceiptNotifications(0);
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Packet Receipt Notif disabled (Op Code = 2, Value = 0)");
for (int i = 0; i <= 2; i++) {
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 + ")");
@ -284,7 +286,7 @@ import no.nordicsemi.android.error.SecureDfuError;
// 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() & 0xFFFFFFFL);
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
@ -293,13 +295,19 @@ import no.nordicsemi.android.error.SecureDfuError;
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) {
if (i < 2) {
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_WARNING, "CRC32 does not match! Retrying...(" + (i + 2) + "/3)");
logi("CRC32 does not match! Retrying...(" + (i + 2) + "/3)");
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) {
@ -313,8 +321,6 @@ import no.nordicsemi.android.error.SecureDfuError;
mService.terminateConnection(gatt, DfuBaseService.ERROR_CRC_ERROR);
return;
}
} else {
break;
}
}
@ -322,6 +328,29 @@ import no.nordicsemi.android.error.SecureDfuError;
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
@ -332,30 +361,79 @@ import no.nordicsemi.android.error.SecureDfuError;
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);
mProgressInfo.setBytesSent(0);
// TODO resume?
// Number of chunks in which the data will be sent
final int count = (mImageSizeInBytes + info.maxSize - 1) / info.maxSize;
// Chunk iterator
int i = 1;
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) {
// Create the Data object
logi("Creating Data object (Op Code = 1, Type = 2, Size = " + mProgressInfo.getAvailableObjectSizeIsBytes() + ") (" + i + "/" + count + ")");
writeCreateRequest(OBJECT_DATA, mProgressInfo.getAvailableObjectSizeIsBytes());
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Data object (" + i + "/" + count + ") created");
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...");
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Uploading firmware...");
uploadFirmwareImage(mPacketCharacteristic);
} catch (final DeviceDisconnectedException e) {
loge("Disconnected while sending data");
@ -365,17 +443,40 @@ import no.nordicsemi.android.error.SecureDfuError;
// Calculate Checksum
logi("Sending Calculate Checksum command (Op Code = 3)");
checksum = readChecksum();
// TODO validate
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));
// Execute Init packet
logi("Executing data object (Op Code = 4)");
writeExecute();
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Data object executed");
// 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++;
// 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");

View File

@ -247,6 +247,7 @@ public class ArchiveInputStream extends ZipInputStream {
throw new IOException("The ZIP file must contain an Application, a Soft Device and/or a Bootloader.");
}
}
mark(0);
} finally {
super.close();
}
@ -351,19 +352,27 @@ public class ArchiveInputStream extends ZipInputStream {
@Override
public void reset() throws IOException {
if (applicationBytes != null && (softDeviceBytes != null || bootloaderBytes != null || softDeviceAndBootloaderBytes != null))
throw new UnsupportedOperationException("Application must be sent in a separate connection.");
currentSource = markedSource;
bytesReadFromCurrentSource = bytesReadFromMarkedSource;
bytesRead = bytesReadFromCurrentSource = bytesReadFromMarkedSource;
// Restore the CRC to the value is was on mark.
crc32.reset();
if (currentSource == bootloaderBytes && softDeviceBytes != null) {
crc32.update(softDeviceBytes);
bytesRead += softDeviceSize;
}
crc32.update(currentSource, 0, bytesReadFromCurrentSource);
}
public int getCrc32() {
return (int) (crc32.getValue() & 0xFFFFFFFFL);
/**
* Returns the CRC32 of the part of the firmware that was already read.
* @return the CRC
*/
public long getCrc32() {
return crc32.getValue();
}
/**
@ -424,6 +433,7 @@ public class ArchiveInputStream extends ZipInputStream {
applicationBytes = null;
applicationSize = 0;
}
mark(0);
return t;
}