diff --git a/lib/analytics/src/main/java/no/nordicsemi/android/dfu/analytics/Events.kt b/lib/analytics/src/main/java/no/nordicsemi/android/dfu/analytics/Events.kt index d606f068..8cb6e4ff 100644 --- a/lib/analytics/src/main/java/no/nordicsemi/android/dfu/analytics/Events.kt +++ b/lib/analytics/src/main/java/no/nordicsemi/android/dfu/analytics/Events.kt @@ -110,6 +110,12 @@ class PrepareDataObjectDelaySettingsEvent(private val delay: Int) : DFUSettingsC override fun createBundle() = bundleOf(FirebaseParam.VALUE to delay) } +class ExecuteInitDelaySettingsEvent(private val delay: Int) : DFUSettingsChangeEvent { + override val eventName: String = "EXECUTE_INIT_DELAY_EVENT" + + override fun createBundle() = bundleOf(FirebaseParam.VALUE to delay) +} + class RebootTimeSettingsEvent(private val rebootTime: Int) : DFUSettingsChangeEvent { override val eventName: String = "REBOOT_TIME_EVENT" diff --git a/lib/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java b/lib/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java index d2fc0c4f..8dad9932 100644 --- a/lib/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java +++ b/lib/dfu/src/main/java/no/nordicsemi/android/dfu/DfuBaseService.java @@ -278,6 +278,11 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres * 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"; + /** + * The duration of a delay that will be added after sending the Init Packet and before + * executing it, in milliseconds. This defaults to 0 for backwards compatibility reason. + */ + public static final String EXTRA_INIT_EXECUTE_DELAY = "no.nordicsemi.android.dfu.extra.EXTRA_INIT_EXECUTE_DELAY"; /** * This property must contain a boolean value. *
diff --git a/lib/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceInitiator.java b/lib/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceInitiator.java index 7c20c360..48d4c3bc 100644 --- a/lib/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceInitiator.java +++ b/lib/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceInitiator.java @@ -108,6 +108,7 @@ public final class DfuServiceInitiator { private int numberOfRetries = 0; // 0 to be backwards compatible private int mbrSize = DEFAULT_MBR_SIZE; private long dataObjectDelay = 0; // initially disabled + private long executeInitDelay = 0; // initially disabled private long rebootTime = 0; // ms private long scanTimeout = DEFAULT_SCAN_TIMEOUT; // ms @@ -245,6 +246,19 @@ public DfuServiceInitiator setPrepareDataObjectDelay(final long delay) { return this; } + /** + * This method sets the duration of a delay, that the service will wait after sending the + * init packet and before executing it. The delay will be done after the init packet is sent + * and before sending the execute command. The default value is 0, which disables this feature. + * + * @param delay the delay that the service will wait before executing the init packet in milliseconds. + * @return the builder + */ + public DfuServiceInitiator setExecuteInitDelay(final long delay) { + this.executeInitDelay = delay; + return this; + } + /** * Enables or disables the Packet Receipt Notification (PRN) procedure. *
@@ -870,6 +884,7 @@ public DfuServiceController start(@NonNull final Context context, @NonNull final
intent.putExtra(DfuBaseService.EXTRA_MAX_DFU_ATTEMPTS, numberOfRetries);
intent.putExtra(DfuBaseService.EXTRA_MBR_SIZE, mbrSize);
intent.putExtra(DfuBaseService.EXTRA_DATA_OBJECT_DELAY, dataObjectDelay);
+ intent.putExtra(DfuBaseService.EXTRA_INIT_EXECUTE_DELAY, executeInitDelay);
intent.putExtra(DfuBaseService.EXTRA_SCAN_TIMEOUT, scanTimeout);
intent.putExtra(DfuBaseService.EXTRA_SCAN_DELAY, rebootTime);
if (mtu > 0)
diff --git a/lib/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java b/lib/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java
index 8c5ea8eb..26c9b73e 100644
--- a/lib/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java
+++ b/lib/dfu/src/main/java/no/nordicsemi/android/dfu/SecureDfuImpl.java
@@ -83,6 +83,7 @@ class SecureDfuImpl extends BaseCustomDfuImpl {
private BluetoothGattCharacteristic mPacketCharacteristic;
private long prepareObjectDelay;
+ private long executeInitDelay;
private final SecureBluetoothCallback mBluetoothCallback = new SecureBluetoothCallback();
@@ -223,6 +224,7 @@ public void performDfu(@NonNull final Intent intent)
}
prepareObjectDelay = intent.getLongExtra(DfuBaseService.EXTRA_DATA_OBJECT_DELAY, 0);
+ executeInitDelay = intent.getLongExtra(DfuBaseService.EXTRA_INIT_EXECUTE_DELAY, 0);
try {
// Enable notifications
@@ -472,6 +474,8 @@ private void sendInitPacket(@NonNull final BluetoothGatt gatt, final boolean all
}
// Execute Init packet. It's better to execute it twice than not execute at all...
+ if (executeInitDelay > 0)
+ mService.waitFor(executeInitDelay);
logi("Executing init packet (Op Code = 4)");
writeExecute();
mService.sendLogBroadcast(DfuBaseService.LOG_LEVEL_APPLICATION, "Command object executed");
diff --git a/lib/settings/src/main/java/no/nordicsemi/android/dfu/settings/domain/DFUSettings.kt b/lib/settings/src/main/java/no/nordicsemi/android/dfu/settings/domain/DFUSettings.kt
index 3d2e055a..2454c027 100644
--- a/lib/settings/src/main/java/no/nordicsemi/android/dfu/settings/domain/DFUSettings.kt
+++ b/lib/settings/src/main/java/no/nordicsemi/android/dfu/settings/domain/DFUSettings.kt
@@ -44,6 +44,7 @@ data class DFUSettings(
val externalMcuDfu: Boolean = false,
val disableResume: Boolean = false,
val prepareDataObjectDelay: Int = 0, // ms
+ val executeInitDelay: Int = 0, // ms
val rebootTime: Int = 0, // ms
val scanTimeout: Int = 6_000, // ms
val mtuRequestEnabled: Boolean = true,
diff --git a/lib/settings/src/main/java/no/nordicsemi/android/dfu/settings/repository/SettingsDataSource.kt b/lib/settings/src/main/java/no/nordicsemi/android/dfu/settings/repository/SettingsDataSource.kt
index 6445211d..8a91b6b8 100644
--- a/lib/settings/src/main/java/no/nordicsemi/android/dfu/settings/repository/SettingsDataSource.kt
+++ b/lib/settings/src/main/java/no/nordicsemi/android/dfu/settings/repository/SettingsDataSource.kt
@@ -53,6 +53,7 @@ private val DISABLE_RESUME = booleanPreferencesKey("disable_resume")
private val FORCE_SCANNING_ADDRESS = booleanPreferencesKey("force_scanning_address")
private val NUMBER_OF_POCKETS_KEY = intPreferencesKey("number_of_pockets")
private val PREPARE_OBJECT_DELAY_KEY = intPreferencesKey("prepare_data_object_delay")
+private val EXECUTE_INIT_DELAY_KEY = intPreferencesKey("execute_init_delay")
private val REBOOT_TIME_KEY = intPreferencesKey("reboot_time")
private val SCAN_TIMEOUT_KEY = intPreferencesKey("scan_timeout")
private val MTU = booleanPreferencesKey("requestMtu")
@@ -75,6 +76,7 @@ class SettingsDataSource @Inject constructor(
context.dataStore.edit {
it[PACKETS_RECEIPT_NOTIFICATION_KEY] = settings.packetsReceiptNotification
it[PREPARE_OBJECT_DELAY_KEY] = settings.prepareDataObjectDelay
+ it[EXECUTE_INIT_DELAY_KEY] = settings.executeInitDelay
it[REBOOT_TIME_KEY] = settings.rebootTime
it[SCAN_TIMEOUT_KEY] = settings.scanTimeout
it[NUMBER_OF_POCKETS_KEY] = settings.numberOfPackets
@@ -95,6 +97,7 @@ class SettingsDataSource @Inject constructor(
this[EXTERNAL_MCU_KEY] ?: false,
this[DISABLE_RESUME] ?: false,
this[PREPARE_OBJECT_DELAY_KEY] ?: 400,
+ this[EXECUTE_INIT_DELAY_KEY] ?: 0,
this[REBOOT_TIME_KEY] ?: 0,
this[SCAN_TIMEOUT_KEY] ?: 2_000,
this[MTU] ?: true,
diff --git a/profile/main/src/main/java/no/nordicsemi/android/dfu/profile/main/data/DFUManager.kt b/profile/main/src/main/java/no/nordicsemi/android/dfu/profile/main/data/DFUManager.kt
index a88f856e..1921dda7 100644
--- a/profile/main/src/main/java/no/nordicsemi/android/dfu/profile/main/data/DFUManager.kt
+++ b/profile/main/src/main/java/no/nordicsemi/android/dfu/profile/main/data/DFUManager.kt
@@ -76,6 +76,7 @@ internal class DFUManager @Inject constructor(
setForceScanningForNewAddressInLegacyDfu(settings.forceScanningInLegacyDfu)
setPrepareDataObjectDelay(settings.prepareDataObjectDelay.toLong())
+ setExecuteInitDelay(settings.executeInitDelay.toLong())
setRebootTime(settings.rebootTime.toLong())
setScanTimeout(settings.scanTimeout.toLong())
setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(true)
diff --git a/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/view/SettingsScreen.kt b/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/view/SettingsScreen.kt
index 77264022..e8807b63 100644
--- a/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/view/SettingsScreen.kt
+++ b/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/view/SettingsScreen.kt
@@ -157,6 +157,15 @@ internal fun SettingsScreen(
onChange = { onEvent(OnPrepareDataObjectDelayChange(it)) }
)
+ SettingsTimeSlider(
+ text = stringResource(id = R.string.dfu_settings_execute_init_delay),
+ description = stringResource(id = R.string.dfu_settings_execute_init_delay_info),
+ value = state.executeInitDelay,
+ valueRange = 0..1000,
+ stepInMilliseconds = 100, // 0.1 seconds
+ onChange = { onEvent(OnExecuteInitDelayChange(it)) }
+ )
+
Headline(stringResource(id = R.string.dfu_settings_headline_legacy_dfu))
SettingsSwitch(
diff --git a/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/view/SettingsScreenViewEvent.kt b/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/view/SettingsScreenViewEvent.kt
index 742e3b51..2267fb95 100644
--- a/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/view/SettingsScreenViewEvent.kt
+++ b/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/view/SettingsScreenViewEvent.kt
@@ -39,6 +39,8 @@ internal data class OnNumberOfPocketsChange(val numberOfPockets: Int) : Settings
internal data class OnPrepareDataObjectDelayChange(val delay: Int) : SettingsScreenViewEvent
+internal data class OnExecuteInitDelayChange(val delay: Int) : SettingsScreenViewEvent
+
internal data class OnRebootTimeChange(val time: Int) : SettingsScreenViewEvent
internal data class OnScanTimeoutChange(val timeout: Int) : SettingsScreenViewEvent
diff --git a/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/viewmodel/SettingsViewModel.kt b/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/viewmodel/SettingsViewModel.kt
index d49663aa..bbc68fa6 100644
--- a/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/viewmodel/SettingsViewModel.kt
+++ b/profile/settings/src/main/java/no/nordicsemi/android/dfu/profile/settings/viewmodel/SettingsViewModel.kt
@@ -71,6 +71,7 @@ internal class SettingsViewModel @Inject constructor(
OnForceScanningAddressesSwitchClick -> onForceScanningAddressesSwitchClick()
is OnNumberOfPocketsChange -> onNumberOfPocketsChange(event.numberOfPockets)
is OnPrepareDataObjectDelayChange -> onPrepareDataObjectDelayChange(event.delay)
+ is OnExecuteInitDelayChange -> onExecuteInitDelayChange(event.delay)
is OnRebootTimeChange -> onRebootTimeChange(event.time)
is OnScanTimeoutChange -> onScanTimeoutChange(event.timeout)
}
@@ -144,6 +145,14 @@ internal class SettingsViewModel @Inject constructor(
analytics.logEvent(PrepareDataObjectDelaySettingsEvent(newSettings.prepareDataObjectDelay))
}
+ private fun onExecuteInitDelayChange(delay: Int) {
+ val newSettings = state.value.copy(executeInitDelay = delay)
+ viewModelScope.launch {
+ repository.storeSettings(newSettings)
+ }
+ analytics.logEvent(ExecuteInitDelaySettingsEvent(newSettings.executeInitDelay))
+ }
+
private fun onRebootTimeChange(rebootTime: Int) {
val newSettings = state.value.copy(rebootTime = rebootTime)
viewModelScope.launch {
diff --git a/profile/settings/src/main/res/values/strings.xml b/profile/settings/src/main/res/values/strings.xml
index bfcf6e98..59bc371b 100644
--- a/profile/settings/src/main/res/values/strings.xml
+++ b/profile/settings/src/main/res/values/strings.xml
@@ -41,6 +41,8 @@