mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
fix(serial): treat USB unplug and open failure as transient
Serial transports defaulted to isPermanent=true for any disconnect path, including USB unplug and port-open failure. Both conditions can resolve without explicit user re-selection: replug, OS re-enumeration, permission grant. Only an explicit close() (user disconnects) is a true permanent disconnect. - StreamTransport: flip onDeviceDisconnect default isPermanent to false; close() now passes isPermanent=true explicitly. - SerialRadioTransport (Android): pass isPermanent=false explicitly on USB unplug callback path. - SerialTransport (JVM): flip both the open-failure path and the read- loop teardown to isPermanent=false; both are recoverable conditions.
This commit is contained in:
parent
0e47dc6717
commit
a6f8f456fd
3 changed files with 19 additions and 9 deletions
|
|
@ -108,7 +108,10 @@ class SerialRadioTransport(
|
|||
"Uptime: ${uptime}ms, " +
|
||||
"Packets RX: $packetsReceived ($bytesReceived bytes)"
|
||||
}
|
||||
onDeviceDisconnect(false)
|
||||
// USB unplug / cable error is transient — the transport will reconnect when
|
||||
// the device is replugged or the OS re-enumerates the port. Only an explicit
|
||||
// close() (user disconnects) should signal a permanent disconnect.
|
||||
onDeviceDisconnect(waitForStopped = false, isPermanent = false)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ abstract class StreamTransport(protected val callback: RadioTransportCallback, p
|
|||
|
||||
override suspend fun close() {
|
||||
Logger.d { "Closing stream for good" }
|
||||
onDeviceDisconnect(true)
|
||||
onDeviceDisconnect(waitForStopped = true, isPermanent = true)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -45,10 +45,12 @@ abstract class StreamTransport(protected val callback: RadioTransportCallback, p
|
|||
*
|
||||
* @param waitForStopped if true we should wait for the transport to finish - must be false if called from inside
|
||||
* transport callbacks
|
||||
* @param isPermanent true if the device is definitely gone (e.g. USB unplugged), false if it may come back (e.g.
|
||||
* TCP transient disconnect). Defaults to true for serial — subclasses may override with false.
|
||||
* @param isPermanent true only when the user has explicitly disconnected (e.g. [close] was called). USB unplug, I/O
|
||||
* errors, and similar conditions are transient — the transport may recover when the device is replugged or the OS
|
||||
* re-enumerates. Defaults to false so callbacks default to "may come back"; [close] passes true explicitly to
|
||||
* signal a user-initiated terminal disconnect.
|
||||
*/
|
||||
protected open fun onDeviceDisconnect(waitForStopped: Boolean, isPermanent: Boolean = true) {
|
||||
protected open fun onDeviceDisconnect(waitForStopped: Boolean, isPermanent: Boolean = false) {
|
||||
callback.onDisconnect(isPermanent = isPermanent)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,10 @@ private constructor(
|
|||
// Ignore errors during port close
|
||||
}
|
||||
if (isActive) {
|
||||
onDeviceDisconnect(true)
|
||||
// Serial read loop ended unexpectedly (cable unplug, I/O error). Treat as
|
||||
// transient — the user did not explicitly disconnect, and the port may come
|
||||
// back when the device is replugged or the OS re-enumerates it.
|
||||
onDeviceDisconnect(waitForStopped = true, isPermanent = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -169,8 +172,10 @@ private constructor(
|
|||
private const val READ_TIMEOUT_MS = 100
|
||||
|
||||
/**
|
||||
* Creates and opens a [SerialTransport]. If the port cannot be opened, the transport signals a permanent
|
||||
* disconnect to the [callback] and returns the (non-connected) instance.
|
||||
* Creates and opens a [SerialTransport]. If the port cannot be opened, the transport signals a transient
|
||||
* disconnect to the [callback] and returns the (non-connected) instance. The open failure is treated as
|
||||
* non-permanent so higher-layer reconnect orchestration can retry (e.g. when the device is replugged or the
|
||||
* user grants permission); only an explicit close should signal a permanent disconnect.
|
||||
*/
|
||||
fun open(
|
||||
portName: String,
|
||||
|
|
@ -183,7 +188,7 @@ private constructor(
|
|||
if (!transport.startConnection()) {
|
||||
val errorMessage = diagnoseOpenFailure(portName)
|
||||
Logger.w { "[$portName] Serial port could not be opened; signalling disconnect. $errorMessage" }
|
||||
callback.onDisconnect(isPermanent = true, errorMessage = errorMessage)
|
||||
callback.onDisconnect(isPermanent = false, errorMessage = errorMessage)
|
||||
}
|
||||
return transport
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue