feat: snackbar for flash err + disconnect on dfurecovery end
This commit is contained in:
@ -57,6 +57,9 @@ const int universalShifterDfuFlagEncrypted = 0x01;
|
|||||||
const int universalShifterDfuFlagSigned = 0x02;
|
const int universalShifterDfuFlagSigned = 0x02;
|
||||||
const int universalShifterDfuFlagNone = 0x00;
|
const int universalShifterDfuFlagNone = 0x00;
|
||||||
|
|
||||||
|
const String universalShifterBootMetadataWarningMessage =
|
||||||
|
'WARNING: The device failed writing to its internal storage. This might be a temporary problem. If it continues happening, the internal storage may be broken. If so, please contact abawo support';
|
||||||
|
|
||||||
const int errorSequence = 1;
|
const int errorSequence = 1;
|
||||||
const int errorFtmsMissing = 2;
|
const int errorFtmsMissing = 2;
|
||||||
const int errorPairingAuth = 3;
|
const int errorPairingAuth = 3;
|
||||||
|
|||||||
@ -59,11 +59,43 @@ class _BootloaderRecoveryUpdatePageState
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
final service = _firmwareUpdateService;
|
||||||
|
final bluetooth = ref.read(bluetoothProvider).valueOrNull;
|
||||||
|
_firmwareUpdateService = null;
|
||||||
unawaited(_firmwareProgressSubscription?.cancel());
|
unawaited(_firmwareProgressSubscription?.cancel());
|
||||||
unawaited(_firmwareUpdateService?.dispose() ?? Future<void>.value());
|
unawaited(() async {
|
||||||
|
await service?.dispose();
|
||||||
|
await _disconnectBootloaderIfStillConnected(bluetooth: bluetooth);
|
||||||
|
}());
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _disconnectBootloaderIfStillConnected({
|
||||||
|
BluetoothController? bluetooth,
|
||||||
|
}) async {
|
||||||
|
bluetooth ??= ref.read(bluetoothProvider).valueOrNull;
|
||||||
|
if (bluetooth == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final currentState = bluetooth.currentConnectionState;
|
||||||
|
if (currentState.$2 != widget.args.bootloaderDeviceId ||
|
||||||
|
(currentState.$1 != ConnectionStatus.connected &&
|
||||||
|
currentState.$1 != ConnectionStatus.connecting)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await bluetooth.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _dismissToDevices() async {
|
||||||
|
await _disconnectBootloaderIfStillConnected();
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
context.go('/devices');
|
||||||
|
}
|
||||||
|
|
||||||
Future<FirmwareUpdateService?> _ensureFirmwareUpdateService() async {
|
Future<FirmwareUpdateService?> _ensureFirmwareUpdateService() async {
|
||||||
if (_firmwareUpdateService != null) {
|
if (_firmwareUpdateService != null) {
|
||||||
return _firmwareUpdateService;
|
return _firmwareUpdateService;
|
||||||
@ -144,9 +176,24 @@ class _BootloaderRecoveryUpdatePageState
|
|||||||
if (!mounted || result.isOk()) {
|
if (!mounted || result.isOk()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await _disconnectBootloaderIfStillConnected();
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final errorMessage = result.unwrapErr().toString();
|
||||||
setState(() {
|
setState(() {
|
||||||
_firmwareUserMessage = result.unwrapErr().toString();
|
_firmwareUserMessage = errorMessage;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (errorMessage.startsWith(universalShifterBootMetadataWarningMessage)) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text(
|
||||||
|
'WARNING: The device failed writing to its internal storage. This might be a temporary problem. If it continues happening, the internal storage may be broken. If so, please contact abawo support',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _dfuPhaseText(DfuUpdateState state) {
|
String _dfuPhaseText(DfuUpdateState state) {
|
||||||
@ -190,7 +237,7 @@ class _BootloaderRecoveryUpdatePageState
|
|||||||
'0x${_dfuProgress.expectedOffset.toRadixString(16).padLeft(8, '0').toUpperCase()}',
|
'0x${_dfuProgress.expectedOffset.toRadixString(16).padLeft(8, '0').toUpperCase()}',
|
||||||
doneLabel: 'Back to devices',
|
doneLabel: 'Back to devices',
|
||||||
failedLabel: 'Back to devices',
|
failedLabel: 'Back to devices',
|
||||||
onDismiss: () => context.go('/devices'),
|
onDismiss: () => unawaited(_dismissToDevices()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -651,6 +651,19 @@ class _DeviceDetailsPageState extends ConsumerState<DeviceDetailsPage> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (result.isErr() &&
|
||||||
|
result.unwrapErr().toString().startsWith(
|
||||||
|
universalShifterBootMetadataWarningMessage,
|
||||||
|
)) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text(
|
||||||
|
'WARNING: The device failed writing to its internal storage. This might be a temporary problem. If it continues happening, the internal storage may be broken. If so, please contact abawo support',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (result.isOk()) {
|
if (result.isOk()) {
|
||||||
_hasLoadedDeviceTelemetry = false;
|
_hasLoadedDeviceTelemetry = false;
|
||||||
unawaited(_loadDeviceTelemetry(force: true));
|
unawaited(_loadDeviceTelemetry(force: true));
|
||||||
|
|||||||
@ -353,6 +353,9 @@ class FirmwareUpdateService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
_handleStatusPayload(statusResult.unwrap());
|
_handleStatusPayload(statusResult.unwrap());
|
||||||
|
if (_latestStatus?.code == DfuBootloaderStatusCode.bootMetadataError) {
|
||||||
|
throw _DfuFailure(universalShifterBootMetadataWarningMessage);
|
||||||
|
}
|
||||||
_log.info('Initial bootloader DFU status read succeeded');
|
_log.info('Initial bootloader DFU status read succeeded');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -333,6 +333,35 @@ void main() {
|
|||||||
await transport.dispose();
|
await transport.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('fails early on boot metadata error before START', () async {
|
||||||
|
final image = _validImage(40);
|
||||||
|
final transport = _FakeFirmwareUpdateTransport(
|
||||||
|
totalBytes: image.length,
|
||||||
|
initialStatusCode: DfuBootloaderStatusCode.bootMetadataError,
|
||||||
|
);
|
||||||
|
final service = FirmwareUpdateService(
|
||||||
|
transport: transport,
|
||||||
|
defaultStatusTimeout: const Duration(milliseconds: 100),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = await service.startUpdate(
|
||||||
|
imageBytes: image,
|
||||||
|
sessionId: 18,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.isErr(), isTrue);
|
||||||
|
expect(result.unwrapErr().toString(),
|
||||||
|
startsWith(universalShifterBootMetadataWarningMessage));
|
||||||
|
expect(
|
||||||
|
transport.controlWrites
|
||||||
|
.where((write) => write.first == universalShifterDfuOpcodeStart),
|
||||||
|
isEmpty);
|
||||||
|
expect(service.currentProgress.state, DfuUpdateState.failed);
|
||||||
|
|
||||||
|
await service.dispose();
|
||||||
|
await transport.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
test('cancel after START sends session-scoped ABORT', () async {
|
test('cancel after START sends session-scoped ABORT', () async {
|
||||||
final image = _validImage(80);
|
final image = _validImage(80);
|
||||||
final firstFrameSent = Completer<void>();
|
final firstFrameSent = Completer<void>();
|
||||||
@ -373,6 +402,7 @@ void main() {
|
|||||||
class _FakeFirmwareUpdateTransport implements FirmwareUpdateTransport {
|
class _FakeFirmwareUpdateTransport implements FirmwareUpdateTransport {
|
||||||
_FakeFirmwareUpdateTransport({
|
_FakeFirmwareUpdateTransport({
|
||||||
required this.totalBytes,
|
required this.totalBytes,
|
||||||
|
this.initialStatusCode = DfuBootloaderStatusCode.ok,
|
||||||
this.startStatusCode = DfuBootloaderStatusCode.ok,
|
this.startStatusCode = DfuBootloaderStatusCode.ok,
|
||||||
this.alreadyInBootloader = false,
|
this.alreadyInBootloader = false,
|
||||||
this.failEnterBootloader = false,
|
this.failEnterBootloader = false,
|
||||||
@ -387,6 +417,7 @@ class _FakeFirmwareUpdateTransport implements FirmwareUpdateTransport {
|
|||||||
});
|
});
|
||||||
|
|
||||||
final int totalBytes;
|
final int totalBytes;
|
||||||
|
final DfuBootloaderStatusCode initialStatusCode;
|
||||||
final DfuBootloaderStatusCode startStatusCode;
|
final DfuBootloaderStatusCode startStatusCode;
|
||||||
final bool alreadyInBootloader;
|
final bool alreadyInBootloader;
|
||||||
final bool failEnterBootloader;
|
final bool failEnterBootloader;
|
||||||
@ -461,7 +492,7 @@ class _FakeFirmwareUpdateTransport implements FirmwareUpdateTransport {
|
|||||||
@override
|
@override
|
||||||
Future<Result<List<int>>> readStatus() async {
|
Future<Result<List<int>>> readStatus() async {
|
||||||
steps.add('readStatus');
|
steps.add('readStatus');
|
||||||
return Ok(_status(DfuBootloaderStatusCode.ok, 0, 0));
|
return Ok(_status(initialStatusCode, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
Reference in New Issue
Block a user