kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
use only async io when talking to the radio
rodzic
73c2c8def1
commit
e20f7c5943
|
@ -12,7 +12,6 @@ import com.geeksville.concurrent.handledLaunch
|
|||
import com.geeksville.util.anonymize
|
||||
import com.geeksville.util.exceptionReporter
|
||||
import com.geeksville.util.ignoreException
|
||||
import com.geeksville.util.toRemoteExceptions
|
||||
import kotlinx.coroutines.delay
|
||||
import java.lang.reflect.Method
|
||||
import java.util.*
|
||||
|
@ -208,14 +207,22 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Send a packet/command out the radio link
|
||||
override fun handleSendToRadio(p: ByteArray) {
|
||||
override fun handleSendToRadio(a: ByteArray) {
|
||||
safe?.let { s ->
|
||||
val uuid = BTM_TORADIO_CHARACTER
|
||||
debug("queuing ${a.size} bytes to $uuid")
|
||||
|
||||
// Note: we generate a new characteristic each time, because we are about to
|
||||
// change the data and we want the data stored in the closure
|
||||
val toRadio = getCharacteristic(uuid)
|
||||
toRadio.value = a
|
||||
|
||||
s.asyncWriteCharacteristic(toRadio) { r ->
|
||||
try {
|
||||
debug("sending to radio")
|
||||
doWrite(
|
||||
BTM_TORADIO_CHARACTER,
|
||||
p
|
||||
) // Do a synchronous write, so that we can then do our reads if needed
|
||||
r.getOrThrow()
|
||||
debug("write of ${a.size} bytes completed")
|
||||
|
||||
if (isFirstSend) {
|
||||
isFirstSend = false
|
||||
|
@ -225,6 +232,8 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
|||
errormsg("Ignoring sendToRadio exception: $ex")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Attempt to read from the fromRadio mailbox, if data is found broadcast it to android apps
|
||||
|
@ -248,9 +257,8 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
|||
startWatchingFromNum()
|
||||
}
|
||||
} catch (ex: BLEException) {
|
||||
errormsg(
|
||||
"error during doReadFromRadio",
|
||||
ex
|
||||
warn(
|
||||
"error during doReadFromRadio - disconnecting, ${ex.message}"
|
||||
)
|
||||
service.serviceScope.handledLaunch { retryDueToException() }
|
||||
}
|
||||
|
@ -274,12 +282,23 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
|||
/// We only force service refresh the _first_ time we connect to the device. Thereafter it is assumed the firmware didn't change
|
||||
private var hasForcedRefresh = false
|
||||
|
||||
@Volatile
|
||||
var fromNumChanged = false
|
||||
|
||||
private fun startWatchingFromNum() {
|
||||
safe!!.setNotify(fromNum, true) {
|
||||
// We might get multiple notifies before we get around to reading from the radio - so just set one flag
|
||||
fromNumChanged = true
|
||||
debug("fromNum changed")
|
||||
service.serviceScope.handledLaunch {
|
||||
if (fromNumChanged) {
|
||||
fromNumChanged = false
|
||||
debug("fromNum changed, so we are reading new messages")
|
||||
doReadFromRadio(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some buggy BLE stacks can fail on initial connect, with either missing services or missing characteristics. If that happens we
|
||||
|
@ -309,9 +328,11 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
|||
}
|
||||
|
||||
/// We only try to set MTU once, because some buggy implementations fail
|
||||
@Volatile
|
||||
private var shouldSetMtu = true
|
||||
|
||||
/// For testing
|
||||
@Volatile
|
||||
private var isFirstTime = true
|
||||
|
||||
private fun doDiscoverServicesAndInit() {
|
||||
|
@ -410,22 +431,6 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
|||
lostConnectCb = { service.onDisconnect(isPermanent = false) })
|
||||
}
|
||||
|
||||
/**
|
||||
* do a synchronous write operation
|
||||
*/
|
||||
private fun doWrite(uuid: UUID, a: ByteArray) = toRemoteExceptions {
|
||||
safe?.let { s ->
|
||||
debug("queuing ${a.size} bytes to $uuid")
|
||||
|
||||
// Note: we generate a new characteristic each time, because we are about to
|
||||
// change the data and we want the data stored in the closure
|
||||
val toRadio = getCharacteristic(uuid)
|
||||
toRadio.value = a
|
||||
|
||||
s.writeCharacteristic(toRadio)
|
||||
debug("write of ${a.size} bytes completed")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a chracteristic, but in a safe manner because some buggy BLE implementations might return null
|
||||
|
|
|
@ -58,12 +58,18 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
|
|||
/// Users can access the GATT directly as needed
|
||||
var gatt: BluetoothGatt? = null
|
||||
|
||||
@Volatile
|
||||
var state = BluetoothProfile.STATE_DISCONNECTED
|
||||
|
||||
@Volatile
|
||||
private var currentWork: BluetoothContinuation? = null
|
||||
private val workQueue = mutableListOf<BluetoothContinuation>()
|
||||
|
||||
// Called for reconnection attemps
|
||||
@Volatile
|
||||
private var connectionCallback: ((Result<Unit>) -> Unit)? = null
|
||||
|
||||
@Volatile
|
||||
private var lostConnectCallback: (() -> Unit)? = null
|
||||
|
||||
/// from characteristic UUIDs to the handler function for notfies
|
||||
|
@ -123,13 +129,17 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
|
|||
debug("Starting work: $tag")
|
||||
return startWorkFn()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return super.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* skanky hack to restart BLE if it says it is hosed
|
||||
* https://stackoverflow.com/questions/35103701/ble-android-onconnectionstatechange-not-being-called
|
||||
*/
|
||||
var mHandler: Handler = Handler()
|
||||
private val mHandler: Handler = Handler()
|
||||
|
||||
fun restartBle() {
|
||||
GeeksvilleApplication.analytics.track("ble_restart") // record # of times we needed to use this nasty hack
|
||||
|
@ -533,7 +543,8 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
|
|||
lostConnectCb: () -> Unit
|
||||
) {
|
||||
logAssert(workQueue.isEmpty())
|
||||
logAssert(currentWork == null) // I don't think anything should be able to sneak in front
|
||||
if (currentWork != null)
|
||||
throw AssertionError("currentWork was not null: $currentWork")
|
||||
|
||||
lostConnectCallback = lostConnectCb
|
||||
connectionCallback = if (autoConnect)
|
||||
|
|
Ładowanie…
Reference in New Issue