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.anonymize
|
||||||
import com.geeksville.util.exceptionReporter
|
import com.geeksville.util.exceptionReporter
|
||||||
import com.geeksville.util.ignoreException
|
import com.geeksville.util.ignoreException
|
||||||
import com.geeksville.util.toRemoteExceptions
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -208,21 +207,31 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a packet/command out the radio link
|
|
||||||
override fun handleSendToRadio(p: ByteArray) {
|
|
||||||
try {
|
|
||||||
debug("sending to radio")
|
|
||||||
doWrite(
|
|
||||||
BTM_TORADIO_CHARACTER,
|
|
||||||
p
|
|
||||||
) // Do a synchronous write, so that we can then do our reads if needed
|
|
||||||
|
|
||||||
if (isFirstSend) {
|
/// Send a packet/command out the radio link
|
||||||
isFirstSend = false
|
override fun handleSendToRadio(a: ByteArray) {
|
||||||
doReadFromRadio(false)
|
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 {
|
||||||
|
r.getOrThrow()
|
||||||
|
debug("write of ${a.size} bytes completed")
|
||||||
|
|
||||||
|
if (isFirstSend) {
|
||||||
|
isFirstSend = false
|
||||||
|
doReadFromRadio(false)
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
errormsg("Ignoring sendToRadio exception: $ex")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
|
||||||
errormsg("Ignoring sendToRadio exception: $ex")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,9 +257,8 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
||||||
startWatchingFromNum()
|
startWatchingFromNum()
|
||||||
}
|
}
|
||||||
} catch (ex: BLEException) {
|
} catch (ex: BLEException) {
|
||||||
errormsg(
|
warn(
|
||||||
"error during doReadFromRadio",
|
"error during doReadFromRadio - disconnecting, ${ex.message}"
|
||||||
ex
|
|
||||||
)
|
)
|
||||||
service.serviceScope.handledLaunch { retryDueToException() }
|
service.serviceScope.handledLaunch { retryDueToException() }
|
||||||
}
|
}
|
||||||
|
@ -274,10 +282,21 @@ 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
|
/// 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
|
private var hasForcedRefresh = false
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
var fromNumChanged = false
|
||||||
|
|
||||||
private fun startWatchingFromNum() {
|
private fun startWatchingFromNum() {
|
||||||
safe!!.setNotify(fromNum, true) {
|
safe!!.setNotify(fromNum, true) {
|
||||||
debug("fromNum changed, so we are reading new messages")
|
// We might get multiple notifies before we get around to reading from the radio - so just set one flag
|
||||||
doReadFromRadio(false)
|
fromNumChanged = true
|
||||||
|
debug("fromNum changed")
|
||||||
|
service.serviceScope.handledLaunch {
|
||||||
|
if (fromNumChanged) {
|
||||||
|
fromNumChanged = false
|
||||||
|
debug("fromNum changed, so we are reading new messages")
|
||||||
|
doReadFromRadio(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,9 +328,11 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We only try to set MTU once, because some buggy implementations fail
|
/// We only try to set MTU once, because some buggy implementations fail
|
||||||
|
@Volatile
|
||||||
private var shouldSetMtu = true
|
private var shouldSetMtu = true
|
||||||
|
|
||||||
/// For testing
|
/// For testing
|
||||||
|
@Volatile
|
||||||
private var isFirstTime = true
|
private var isFirstTime = true
|
||||||
|
|
||||||
private fun doDiscoverServicesAndInit() {
|
private fun doDiscoverServicesAndInit() {
|
||||||
|
@ -410,22 +431,6 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
|
||||||
lostConnectCb = { service.onDisconnect(isPermanent = false) })
|
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
|
* 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
|
/// Users can access the GATT directly as needed
|
||||||
var gatt: BluetoothGatt? = null
|
var gatt: BluetoothGatt? = null
|
||||||
|
|
||||||
|
@Volatile
|
||||||
var state = BluetoothProfile.STATE_DISCONNECTED
|
var state = BluetoothProfile.STATE_DISCONNECTED
|
||||||
|
|
||||||
|
@Volatile
|
||||||
private var currentWork: BluetoothContinuation? = null
|
private var currentWork: BluetoothContinuation? = null
|
||||||
private val workQueue = mutableListOf<BluetoothContinuation>()
|
private val workQueue = mutableListOf<BluetoothContinuation>()
|
||||||
|
|
||||||
// Called for reconnection attemps
|
// Called for reconnection attemps
|
||||||
|
@Volatile
|
||||||
private var connectionCallback: ((Result<Unit>) -> Unit)? = null
|
private var connectionCallback: ((Result<Unit>) -> Unit)? = null
|
||||||
|
|
||||||
|
@Volatile
|
||||||
private var lostConnectCallback: (() -> Unit)? = null
|
private var lostConnectCallback: (() -> Unit)? = null
|
||||||
|
|
||||||
/// from characteristic UUIDs to the handler function for notfies
|
/// 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")
|
debug("Starting work: $tag")
|
||||||
return startWorkFn()
|
return startWorkFn()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return super.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* skanky hack to restart BLE if it says it is hosed
|
* skanky hack to restart BLE if it says it is hosed
|
||||||
* https://stackoverflow.com/questions/35103701/ble-android-onconnectionstatechange-not-being-called
|
* https://stackoverflow.com/questions/35103701/ble-android-onconnectionstatechange-not-being-called
|
||||||
*/
|
*/
|
||||||
var mHandler: Handler = Handler()
|
private val mHandler: Handler = Handler()
|
||||||
|
|
||||||
fun restartBle() {
|
fun restartBle() {
|
||||||
GeeksvilleApplication.analytics.track("ble_restart") // record # of times we needed to use this nasty hack
|
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
|
lostConnectCb: () -> Unit
|
||||||
) {
|
) {
|
||||||
logAssert(workQueue.isEmpty())
|
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
|
lostConnectCallback = lostConnectCb
|
||||||
connectionCallback = if (autoConnect)
|
connectionCallback = if (autoConnect)
|
||||||
|
@ -663,7 +674,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
|
||||||
|
|
||||||
// Cancel any notifications - because when the device comes back it might have forgotten about us
|
// Cancel any notifications - because when the device comes back it might have forgotten about us
|
||||||
notifyHandlers.clear()
|
notifyHandlers.clear()
|
||||||
|
|
||||||
ignoreException {
|
ignoreException {
|
||||||
// Hmm - sometimes the "Connection closing" exception comes back to us - ignore it
|
// Hmm - sometimes the "Connection closing" exception comes back to us - ignore it
|
||||||
failAllWork(BLEException("Connection closing"))
|
failAllWork(BLEException("Connection closing"))
|
||||||
|
|
Ładowanie…
Reference in New Issue