WIP add concept of interface factories

pull/276/head^2
Kevin Hester 2021-03-29 20:20:38 +08:00
rodzic 2ec15bf7b1
commit 2c75d0dee7
7 zmienionych plików z 90 dodań i 36 usunięć

Wyświetl plik

@ -78,7 +78,15 @@ A variable keepAllPackets, if set to true will suppress this behavior and instea
class BluetoothInterface(val service: RadioInterfaceService, val address: String) : IRadioInterface,
Logging {
companion object : Logging {
companion object : Logging, InterfaceFactory('x') {
override fun createInterface(
service: RadioInterfaceService,
rest: String
): IRadioInterface = BluetoothInterface(service, rest)
init {
registerFactory()
}
/// this service UUID is publically visible for scanning
val BTM_SERVICE_UUID = UUID.fromString("6ba1b218-15a8-461f-9fa8-5dcae273eafd")
@ -100,12 +108,12 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
fun toInterfaceName(deviceName: String) = "x$deviceName"
/** Return true if this address is still acceptable. For BLE that means, still bonded */
fun addressValid(context: Context, address: String): Boolean {
override fun addressValid(context: Context, rest: String): Boolean {
val allPaired =
getBluetoothAdapter(context)?.bondedDevices.orEmpty().map { it.address }.toSet()
return if (!allPaired.contains(address)) {
warn("Ignoring stale bond to ${address.anonymize}")
return if (!allPaired.contains(rest)) {
warn("Ignoring stale bond to ${rest.anonymize}")
false
} else
true

Wyświetl plik

@ -0,0 +1,23 @@
package com.geeksville.mesh.service
import android.content.Context
/**
* A base class for the singleton factories that make interfaces. One instance per interface type
*/
abstract class InterfaceFactory(val prefix: Char) {
companion object {
private val factories = mutableMapOf<Char, InterfaceFactory>()
fun getFactory(l: Char) = factories.get(l)
}
protected fun registerFactory() {
factories[prefix] = this
}
abstract fun createInterface(service: RadioInterfaceService, rest: String): IRadioInterface
/** Return true if this address is still acceptable. For BLE that means, still bonded */
open fun addressValid(context: Context, rest: String): Boolean = true
}

Wyświetl plik

@ -8,9 +8,15 @@ import okhttp3.internal.toHexString
/** A simulated interface that is used for testing in the simulator */
class MockInterface(private val service: RadioInterfaceService) : Logging, IRadioInterface {
companion object : Logging {
companion object : Logging, InterfaceFactory('m') {
override fun createInterface(
service: RadioInterfaceService,
rest: String
): IRadioInterface = MockInterface(service)
const val interfaceName = "m"
init {
registerFactory()
}
}
private var messageCount = 50

Wyświetl plik

@ -1,7 +1,20 @@
package com.geeksville.mesh.service
import com.geeksville.android.Logging
class NopInterface : IRadioInterface {
override fun handleSendToRadio(p: ByteArray) {
companion object : Logging, InterfaceFactory('n') {
override fun createInterface(
service: RadioInterfaceService,
rest: String
): IRadioInterface = NopInterface()
init {
registerFactory()
}
}
override fun handleSendToRadio(p: ByteArray) {
}
override fun close() {

Wyświetl plik

@ -78,7 +78,7 @@ class RadioInterfaceService : Service(), Logging {
// If we are running on the emulator we default to the mock interface, so we can have some data to show to the user
if(address == null && isMockInterfaceAvailable(context))
address = MockInterface.interfaceName
address = MockInterface.prefix.toString()
return address
}
@ -104,13 +104,7 @@ class RadioInterfaceService : Service(), Logging {
if (address != null) {
val c = address[0]
val rest = address.substring(1)
val isValid = when (c) {
'x' -> BluetoothInterface.addressValid(context, rest)
's' -> SerialInterface.addressValid(context, rest)
'n' -> true
'm' -> true
else -> TODO("Unexpected interface type $c")
}
val isValid = InterfaceFactory.getFactory(c)?.addressValid(context, rest) ?: false
if (!isValid)
return null
}
@ -131,8 +125,7 @@ class RadioInterfaceService : Service(), Logging {
*/
var serviceScope = CoroutineScope(Dispatchers.IO + Job())
private val nopIf = NopInterface()
private var radioIf: IRadioInterface = nopIf
private var radioIf: IRadioInterface = NopInterface()
/** true if we have started our interface
*
@ -217,7 +210,7 @@ class RadioInterfaceService : Service(), Logging {
/** Start our configured interface (if it isn't already running) */
private fun startInterface() {
if (radioIf != nopIf)
if (radioIf !is NopInterface)
warn("Can't start interface - $radioIf is already running")
else {
val address = getBondedDeviceAddress(this)
@ -234,26 +227,17 @@ class RadioInterfaceService : Service(), Logging {
val c = address[0]
val rest = address.substring(1)
radioIf = when (c) {
'x' -> BluetoothInterface(this, rest)
's' -> SerialInterface(this, rest)
'm' -> MockInterface(this)
'n' -> nopIf
else -> {
errormsg("Unexpected radio interface type")
nopIf
}
}
radioIf = InterfaceFactory.getFactory(c)?.createInterface(this, rest) ?:
NopInterface()
}
}
}
private fun stopInterface() {
val r = radioIf
info("stopping interface $r")
isStarted = false
radioIf = nopIf
radioIf = NopInterface()
r.close()
// cancel any old jobs and get ready for the new ones
@ -266,7 +250,7 @@ class RadioInterfaceService : Service(), Logging {
receivedPacketsLog.close()
// Don't broadcast disconnects if we were just using the nop device
if (r != nopIf)
if (r !is NopInterface)
onDisconnect(isPermanent = true) // Tell any clients we are now offline
}

Wyświetl plik

@ -21,7 +21,15 @@ import com.hoho.android.usbserial.util.SerialInputOutputManager
*/
class SerialInterface(service: RadioInterfaceService, private val address: String) :
StreamInterface(service), Logging, SerialInputOutputManager.Listener {
companion object : Logging {
companion object : Logging, InterfaceFactory('s') {
override fun createInterface(
service: RadioInterfaceService,
rest: String
): IRadioInterface = SerialInterface(service, rest)
init {
registerFactory()
}
/**
* according to https://stackoverflow.com/questions/12388914/usb-device-access-pop-up-suppression/15151075#15151075
@ -41,14 +49,14 @@ class SerialInterface(service: RadioInterfaceService, private val address: Strin
return drivers
}
fun addressValid(context: Context, rest: String): Boolean {
override fun addressValid(context: Context, rest: String): Boolean {
findSerial(context, rest)?.let { d ->
return assumePermission || context.usbManager.hasPermission(d.device)
}
return false
}
fun findSerial(context: Context, rest: String): UsbSerialDriver? {
private fun findSerial(context: Context, rest: String): UsbSerialDriver? {
val drivers = findDrivers(context)
return if (drivers.isEmpty())
@ -61,7 +69,7 @@ class SerialInterface(service: RadioInterfaceService, private val address: Strin
private var uart: UsbSerialDriver? = null
private var ioManager: SerialInputOutputManager? = null
var usbReceiver = object : BroadcastReceiver() {
private var usbReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) = exceptionReporter {
if (UsbManager.ACTION_USB_DEVICE_DETACHED == intent.action) {

Wyświetl plik

@ -1,5 +1,6 @@
package com.geeksville.mesh.service
import com.geeksville.android.Logging
import java.io.*
import java.net.InetAddress
import java.net.Socket
@ -9,7 +10,18 @@ import kotlin.concurrent.thread
class TCPInterface(service: RadioInterfaceService, private val address: String) :
StreamInterface(service) {
var socket: Socket? = null
companion object : Logging, InterfaceFactory('t') {
override fun createInterface(
service: RadioInterfaceService,
rest: String
): IRadioInterface = TCPInterface(service, rest)
init {
registerFactory()
}
}
var socket: Socket? = null
lateinit var outStream: OutputStream
lateinit var inStream: InputStream