Refactor USB connection handling to use scheduled closure and improve error management in USB services

This commit is contained in:
just_stuff_tm 2026-03-02 04:11:52 -05:00 committed by just-stuff-tm
parent 4b24506310
commit dcad5c586d
4 changed files with 81 additions and 24 deletions

View file

@ -105,11 +105,8 @@ class MainActivity : FlutterActivity() {
"connect" -> handleUsbConnect(call, result)
"write" -> handleUsbWrite(call, result)
"disconnect" -> {
usbIoExecutor.execute {
closeUsbConnection()
mainHandler.post {
result.success(null)
}
scheduleCloseUsbConnection {
result.success(null)
}
}
else -> result.notImplemented()
@ -315,7 +312,7 @@ class MainActivity : FlutterActivity() {
null,
)
}
closeUsbConnection()
scheduleCloseUsbConnection()
}
},
).also { manager ->
@ -338,6 +335,16 @@ class MainActivity : FlutterActivity() {
return driver.ports.firstOrNull()
}
private fun scheduleCloseUsbConnection(onComplete: (() -> Unit)? = null) {
usbIoExecutor.execute {
closeUsbConnection()
if (onComplete != null) {
mainHandler.post(onComplete)
}
}
}
@Synchronized
private fun closeUsbConnection() {
try {
ioManager?.stop()
@ -381,12 +388,13 @@ class MainActivity : FlutterActivity() {
}
if (connectedDeviceName == detachedName) {
closeUsbConnection()
eventSink?.error(
"usb_device_detached",
"USB device was disconnected",
null,
)
scheduleCloseUsbConnection {
eventSink?.error(
"usb_device_detached",
"USB device was disconnected",
null,
)
}
}
}

View file

@ -1266,14 +1266,23 @@ class MeshCoreConnector extends ChangeNotifier {
_selfInfoRetryTimer?.cancel();
if (PlatformInfo.isWeb &&
_activeTransport == MeshCoreTransportType.bluetooth) {
_selfInfoRetryTimer = Timer(const Duration(seconds: 10), () {
var attempts = 0;
const maxAttempts = 3;
_selfInfoRetryTimer = Timer.periodic(const Duration(seconds: 10), (
timer,
) {
if (!isConnected || !_awaitingSelfInfo) {
timer.cancel();
return;
}
if (_isLoadingContacts || _isSyncingChannels || _channelSyncInFlight) {
return;
}
attempts += 1;
unawaited(sendFrame(buildAppStartFrame()));
if (attempts >= maxAttempts) {
timer.cancel();
}
});
return;
}

View file

@ -186,8 +186,7 @@ class UsbSerialService {
}
void dispose() {
unawaited(disconnect());
unawaited(_frameController.close());
unawaited(disconnect().whenComplete(_closeFrameController));
}
void _handleSerialData(FlSerialEventArgs event) {
@ -197,7 +196,7 @@ class UsbSerialService {
_ingestRawBytes(Uint8List.fromList(bytes));
}
} catch (error, stack) {
_frameController.addError(error, stack);
_addFrameError(error, stack);
}
}
@ -210,13 +209,13 @@ class UsbSerialService {
_ingestRawBytes(data.buffer.asUint8List());
return;
}
_frameController.addError(
_addFrameError(
StateError('Unexpected Android USB event payload: ${data.runtimeType}'),
);
}
void _handleSerialError(Object error, [StackTrace? stackTrace]) {
_frameController.addError(error, stackTrace);
_addFrameError(error, stackTrace);
}
void _handleSerialDone() {
@ -231,10 +230,31 @@ class UsbSerialService {
);
continue;
}
_frameController.add(packet.payload);
_addFrame(packet.payload);
}
}
void _addFrame(Uint8List payload) {
if (_frameController.isClosed) {
return;
}
_frameController.add(payload);
}
void _addFrameError(Object error, [StackTrace? stackTrace]) {
if (_frameController.isClosed) {
return;
}
_frameController.addError(error, stackTrace);
}
Future<void> _closeFrameController() async {
if (_frameController.isClosed) {
return;
}
await _frameController.close();
}
void _logFrameSummary(String prefix, Uint8List bytes) {
if (bytes.isEmpty) {
debugPrint('$prefix len=0');

View file

@ -160,8 +160,7 @@ class UsbSerialService {
}
void dispose() {
unawaited(disconnect());
unawaited(_frameController.close());
unawaited(disconnect().whenComplete(_closeFrameController));
}
Future<List<JSObject>> _getAuthorizedPorts() async {
@ -285,12 +284,12 @@ class UsbSerialService {
}
} catch (error, stackTrace) {
if (_status == UsbSerialStatus.connected) {
_frameController.addError(error, stackTrace);
_addFrameError(error, stackTrace);
}
} finally {
_releaseLock(reader);
if (_status == UsbSerialStatus.connected && identical(reader, _reader)) {
_frameController.addError(StateError('USB serial connection closed'));
_addFrameError(StateError('USB serial connection closed'));
}
}
}
@ -402,10 +401,31 @@ class UsbSerialService {
);
continue;
}
_frameController.add(packet.payload);
_addFrame(packet.payload);
}
}
void _addFrame(Uint8List payload) {
if (_frameController.isClosed) {
return;
}
_frameController.add(payload);
}
void _addFrameError(Object error, [StackTrace? stackTrace]) {
if (_frameController.isClosed) {
return;
}
_frameController.addError(error, stackTrace);
}
Future<void> _closeFrameController() async {
if (_frameController.isClosed) {
return;
}
await _frameController.close();
}
void _logFrameSummary(String prefix, Uint8List bytes) {
if (bytes.isEmpty) {
debugPrint('$prefix len=0');