fix: fix dfu id mismatch because stale notification

This commit is contained in:
2026-05-05 20:44:31 +02:00
parent 512c31d356
commit 230a6838e0
2 changed files with 66 additions and 4 deletions

View File

@ -527,10 +527,33 @@ class FirmwareUpdateService {
Future<DfuBootloaderStatus> _sendStartAndWaitForStatus(
BootloaderDfuStartPayload payload, {
required Duration timeout,
}) {
return _writeControlAndWaitForStatus(
BootloaderDfuProtocol.encodeStartPayload(payload),
}) async {
final eventCount = _statusEventCount;
final encodedPayload = BootloaderDfuProtocol.encodeStartPayload(payload);
_log.fine(
'Writing DFU START command for session ${payload.sessionId} '
'(len=${encodedPayload.length})',
);
final result = await _transport.writeControl(encodedPayload);
if (result.isErr()) {
_log.warning('DFU START write failed: ${result.unwrapErr()}');
throw _DfuFailure(
'Failed to write bootloader control command: ${result.unwrapErr()}',
);
}
return _waitForStatus(
afterEventCount: eventCount,
timeout: timeout,
acceptStatus: (status) {
if (status.sessionId == payload.sessionId) {
return true;
}
_log.fine(
'Ignoring stale START status for session ${status.sessionId}; '
'waiting for session ${payload.sessionId}',
);
return false;
},
);
}
@ -708,6 +731,7 @@ class FirmwareUpdateService {
required int afterEventCount,
required Duration timeout,
bool recoverable = false,
bool Function(DfuBootloaderStatus status)? acceptStatus,
}) async {
final deadline = DateTime.now().add(timeout);
var observedEvents = afterEventCount;
@ -722,7 +746,12 @@ class FirmwareUpdateService {
'session=${_latestStatus!.sessionId}, offset=${_latestStatus!.expectedOffset}, '
'code=${_statusLabel(_latestStatus!)}',
);
return _latestStatus!;
final status = _latestStatus!;
if (acceptStatus == null || acceptStatus(status)) {
return status;
}
observedEvents = _statusEventCount;
continue;
}
final remaining = deadline.difference(DateTime.now());

View File

@ -307,6 +307,33 @@ void main() {
await transport.dispose();
});
test('ignores stale previous-session status while waiting for START',
() async {
final image = _validImage(80);
final transport = _FakeFirmwareUpdateTransport(
totalBytes: image.length,
staleStartStatusSessionId: 20,
);
final service = FirmwareUpdateService(
transport: transport,
defaultStatusTimeout: const Duration(milliseconds: 100),
);
final result = await service.startUpdate(
imageBytes: image,
sessionId: 21,
);
expect(result.isOk(), isTrue);
expect(
transport.controlWrites.first.first, universalShifterDfuOpcodeStart);
expect(transport.dataWrites.first[0], 21);
expect(service.currentProgress.state, DfuUpdateState.completed);
await service.dispose();
await transport.dispose();
});
test('fails with bootloader status error on rejected START', () async {
final image = _validImage(40);
final transport = _FakeFirmwareUpdateTransport(
@ -410,6 +437,7 @@ class _FakeFirmwareUpdateTransport implements FirmwareUpdateTransport {
this.suppressFirstDataStatus = false,
this.failDataWriteAtOffsetOnce,
this.resetSessionOnRecoveryStatus = false,
this.staleStartStatusSessionId,
this.suppressFinishStatus = false,
this.disconnectAfterFinish = true,
this.finishStatusCode = DfuBootloaderStatusCode.ok,
@ -425,6 +453,7 @@ class _FakeFirmwareUpdateTransport implements FirmwareUpdateTransport {
final bool suppressFirstDataStatus;
final int? failDataWriteAtOffsetOnce;
final bool resetSessionOnRecoveryStatus;
final int? staleStartStatusSessionId;
final bool suppressFinishStatus;
final bool disconnectAfterFinish;
final DfuBootloaderStatusCode finishStatusCode;
@ -502,6 +531,10 @@ class _FakeFirmwareUpdateTransport implements FirmwareUpdateTransport {
if (opcode == universalShifterDfuOpcodeStart) {
_sessionId = payload[17];
_expectedOffset = 0;
final staleSessionId = staleStartStatusSessionId;
if (staleSessionId != null) {
_scheduleStatus(DfuBootloaderStatusCode.ok, staleSessionId, 0);
}
_scheduleStatus(startStatusCode, _sessionId, 0);
} else if (opcode == universalShifterDfuOpcodeGetStatus) {
if (resetSessionOnRecoveryStatus && _connectCount > 1) {