diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java b/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java index 427996c..0ab16ab 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java @@ -265,6 +265,11 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres * It is recommended to use this new service when SDK 13 (or later) is out. */ public static final String EXTRA_UNSAFE_EXPERIMENTAL_BUTTONLESS_DFU = "no.nordicsemi.android.dfu.extra.EXTRA_UNSAFE_EXPERIMENTAL_BUTTONLESS_DFU"; + /** + * The duration of a delay that will be added before sending each data packet in Secure DFU, + * in milliseconds. This defaults to 0 for backwards compatibility reason. + */ + public static final String EXTRA_DATA_OBJECT_DELAY = "no.nordicsemi.android.dfu.extra.EXTRA_DATA_OBJECT_DELAY"; /** * This property must contain a boolean value. *
diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceInitiator.java b/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceInitiator.java index 5b4ea76..e2694f7 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceInitiator.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceInitiator.java @@ -82,6 +82,7 @@ public final class DfuServiceInitiator { private boolean disableResume = false; private int numberOfRetries = 0; // 0 to be backwards compatible private int mbrSize = DEFAULT_MBR_SIZE; + private long dataObjectDelay = 0; // initially disabled private Boolean packetReceiptNotificationsEnabled; private int numberOfPackets = 12; @@ -183,6 +184,30 @@ public final class DfuServiceInitiator { return this; } + /** + * This method sets the duration of a delay, that the service will wait before sending each + * data object in Secure DFU. The delay will be done after a data object is created, and before + * any data byte is sent. The default value is 0, which disables this feature. + *
+ * It has been found, that a delay of at least 300ms reduces the risk of packet lose (the + * bootloader needs some time to prepare flash memory) on DFU bootloader from SDK 15 and 16. + * The delay does not have to be longer than 400 ms, as according to performed tests, such delay + * is sufficient. + *
+ * The longer the delay, the more time DFU will take to complete (delay will be repeated for + * each data object (4096 bytes)). However, with too small delay a packet lose may occur, + * causing the service to enable PRN and set them to 1 making DFU process very, very slow + * (but reliable). + * + * @param delay the initial delay that the service will wait before sending each data object. + * @since 1.10 + * @return the builder + */ + public DfuServiceInitiator setPrepareDataObjectDelay(final long delay) { + this.dataObjectDelay = delay; + return this; + } + /** * Enables or disables the Packet Receipt Notification (PRN) procedure. *
@@ -759,6 +784,7 @@ public final class DfuServiceInitiator { intent.putExtra(DfuBaseService.EXTRA_DISABLE_RESUME, disableResume); intent.putExtra(DfuBaseService.EXTRA_MAX_DFU_ATTEMPTS, numberOfRetries); intent.putExtra(DfuBaseService.EXTRA_MBR_SIZE, mbrSize); + intent.putExtra(DfuBaseService.EXTRA_DATA_OBJECT_DELAY, dataObjectDelay); if (mtu > 0) intent.putExtra(DfuBaseService.EXTRA_MTU, mtu); intent.putExtra(DfuBaseService.EXTRA_CURRENT_MTU, currentMtu); 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 e0340d2..da71799 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java @@ -82,6 +82,8 @@ class SecureDfuImpl extends BaseCustomDfuImpl { private BluetoothGattCharacteristic mControlPointCharacteristic; private BluetoothGattCharacteristic mPacketCharacteristic; + private long prepareObjectDelay; + private final SecureBluetoothCallback mBluetoothCallback = new SecureBluetoothCallback(); protected class SecureBluetoothCallback extends BaseCustomBluetoothCallback { @@ -224,6 +226,8 @@ class SecureDfuImpl extends BaseCustomDfuImpl { requestMtu(requiredMtu); } + prepareObjectDelay = intent.getLongExtra(DfuBaseService.EXTRA_DATA_OBJECT_DELAY, 0); + try { // Enable notifications enableCCCD(mControlPointCharacteristic, NOTIFICATIONS); @@ -580,8 +584,7 @@ class SecureDfuImpl extends BaseCustomDfuImpl { mProgressInfo.setBytesSent(0); } - final long initialDelay = 400; // ms - final long startTime = SystemClock.elapsedRealtime() + initialDelay; + final long startTime = SystemClock.elapsedRealtime(); if (info.offset < mImageSizeInBytes) { int attempt = 1; @@ -594,10 +597,10 @@ class SecureDfuImpl extends BaseCustomDfuImpl { writeCreateRequest(OBJECT_DATA, availableObjectSizeInBytes); mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Data object (" + (currentChunk + 1) + "/" + chunkCount + ") created"); - if (currentChunk == 0) { - // Waiting until the device is ready to receive first data object. - mService.waitFor(initialDelay); - } + // Waiting until the device is ready to receive the data object. + // If prepare data object delay was set in the initiator, the delay will be used + // for all data objects. + mService.waitFor(prepareObjectDelay > 0 ? prepareObjectDelay : chunkCount == 0 ? 400 : 0); mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Uploading firmware..."); } else {