new bluetooth work queue stuff seems to work

pull/8/head
geeksville 2020-01-27 16:00:00 -08:00
rodzic 6edc89e2aa
commit 6cebf063d7
6 zmienionych plików z 100 dodań i 70 usunięć

Wyświetl plik

@ -68,7 +68,6 @@
<!-- This is a private service which just does direct communication to the radio -->
<service
android:name="com.geeksville.mesh.RadioInterfaceService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="true"
android:exported="false" />

Wyświetl plik

@ -0,0 +1,9 @@
// IRadioInterfaceService.aidl
package com.geeksville.mesh;
// Declare any non-default types here with import statements
interface IRadioInterfaceService {
void sendToRadio(in byte [] a);
}

Wyświetl plik

@ -151,12 +151,14 @@ class MainActivity : AppCompatActivity(), Logging {
}
var meshService: IMeshService? = null
var isBound = false
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
val m = IMeshService.Stub.asInterface(service)
meshService = m
// FIXME - don't do these operations until we are informed we have a connection, otherwise
// the radio interface service might not yet be connected to the mesh service
// Do some test operations
m.setOwner("+16508675309", "Kevin Xter", "kx")
@ -189,19 +191,16 @@ class MainActivity : AppCompatActivity(), Logging {
//val intent = Intent(this, MeshService::class.java)
//intent.action = IMeshService::class.java.name
isBound = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
logAssert(isBound)
logAssert(bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE))
}
private fun unbindMeshService() {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
// if we never connected, do nothing
if (isBound) {
debug("Unbinding from mesh service!")
unbindService(serviceConnection)
meshService = null
}
debug("Unbinding from mesh service!")
unbindService(serviceConnection)
meshService = null
}
override fun onPause() {

Wyświetl plik

@ -1,10 +1,7 @@
package com.geeksville.mesh
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.*
import android.os.IBinder
import com.geeksville.android.Logging
import com.geeksville.mesh.MeshProtos.MeshPacket
@ -39,6 +36,8 @@ class MeshService : Service(), Logging {
/// A mapping of receiver class name to package name - used for explicit broadcasts
private val clientPackages = mutableMapOf<String, String>()
private var radioService: IRadioInterfaceService? = null
/*
see com.geeksville.mesh broadcast intents
// RECEIVED_OPAQUE for data received from other nodes
@ -76,34 +75,59 @@ class MeshService : Service(), Logging {
/// Send a command/packet to our radio
private fun sendToRadio(p: ToRadio.Builder) {
RadioInterfaceService.sendToRadio(this, p.build().toByteArray())
radioService!!.sendToRadio(p.build().toByteArray())
}
override fun onBind(intent: Intent?): IBinder? {
return binder
}
private val radioConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val filter = IntentFilter(RadioInterfaceService.RECEIVE_FROMRADIO_ACTION)
registerReceiver(radioInterfaceReceiver, filter)
radioReceiverRegistered = true
val m = IRadioInterfaceService.Stub.asInterface(service)
radioService = m
// FIXME - don't do this until after we see that the radio is connected to the phone
val sim = SimRadio(this@MeshService)
sim.start() // Fake up our node id info and some past packets from other nodes
// Ask for the current node DB
sendToRadio(ToRadio.newBuilder().apply {
wantNodes = ToRadio.WantNodes.newBuilder().build()
})
}
override fun onServiceDisconnected(name: ComponentName?) {
radioService = null
}
}
override fun onCreate() {
super.onCreate()
info("Creating mesh service")
val filter = IntentFilter(RadioInterfaceService.RECEIVE_FROMRADIO_ACTION)
registerReceiver(radioInterfaceReceiver, filter)
// FIXME - don't do this until after we see that the radio is connected to the phone
val sim = SimRadio(this)
sim.start() // Fake up our node id info and some past packets from other nodes
// Ask for the current node DB
sendToRadio(ToRadio.newBuilder().apply {
wantNodes = ToRadio.WantNodes.newBuilder().build()
})
// We in turn need to use the radiointerface service
val intent = Intent(this, RadioInterfaceService::class.java)
// intent.action = IMeshService::class.java.name
logAssert(bindService(intent, radioConnection, Context.BIND_AUTO_CREATE))
// the rest of our init will happen once we are in radioConnection.onServiceConnected
}
private var radioReceiverRegistered = false
override fun onDestroy() {
info("Destroying mesh service")
unregisterReceiver(radioInterfaceReceiver)
if (radioReceiverRegistered)
unregisterReceiver(radioInterfaceReceiver)
unbindService(radioConnection)
radioService = null
super.onDestroy()
}

Wyświetl plik

@ -1,11 +1,13 @@
package com.geeksville.mesh
import android.app.Service
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.Intent
import androidx.core.app.JobIntentService
import android.os.IBinder
import com.geeksville.android.DebugLogFile
import com.geeksville.android.Logging
import com.google.protobuf.util.JsonFormat
@ -66,20 +68,9 @@ A variable keepAllPackets, if set to true will suppress this behavior and instea
* Note - this class intentionally dumb. It doesn't understand protobuf framing etc...
* It is designed to be simple so it can be stubbed out with a simulated version as needed.
*/
class RadioInterfaceService : JobIntentService(), Logging {
class RadioInterfaceService : Service(), Logging {
companion object {
/**
* Unique job ID for this service. Must be the same for all work.
*/
private const val JOB_ID = 1001
/**
* The SEND_TORADIO
* Payload will be the raw bytes which were contained within a MeshProtos.ToRadio protobuf
*/
const val SEND_TORADIO_ACTION = "$prefix.SEND_TORADIO"
/**
* The RECEIVED_FROMRADIO
* Payload will be the raw bytes which were contained within a MeshProtos.FromRadio protobuf
@ -94,26 +85,6 @@ class RadioInterfaceService : JobIntentService(), Logging {
private val BTM_FROMNUM_CHARACTER =
UUID.fromString("ed9da18c-a800-4f66-a670-aa7547e34453")
/**
* Convenience method for enqueuing work in to this service.
*/
fun enqueueWork(context: Context, work: Intent) {
enqueueWork(
context,
RadioInterfaceService::class.java, JOB_ID, work
)
}
/// Helper function to send a packet to the radio
fun sendToRadio(context: Context, a: ByteArray) {
val i = Intent(SEND_TORADIO_ACTION)
i.putExtra(EXTRA_PAYLOAD, a)
enqueueWork(context, i)
}
// for debug logging only
private val jsonPrinter = JsonFormat.printer()
/// This is public only so that SimRadio can bootstrap our message flow
fun broadcastReceivedFromRadio(context: Context, payload: ByteArray) {
val intent = Intent(RECEIVE_FROMRADIO_ACTION)
@ -131,8 +102,15 @@ class RadioInterfaceService : JobIntentService(), Logging {
private lateinit var device: BluetoothDevice
private lateinit var safe: SafeBluetooth
private lateinit var fromRadio: BluetoothGattCharacteristic
private lateinit var toRadio: BluetoothGattCharacteristic
private lateinit var fromNum: BluetoothGattCharacteristic
lateinit var sentPacketsLog: DebugLogFile // inited in onCreate
// for debug logging only
private val jsonPrinter = JsonFormat.printer()
fun broadcastConnectionChanged(isConnected: Boolean) {
val intent = Intent("$prefix.CONNECTION_CHANGED")
intent.putExtra(EXTRA_CONNECTED, isConnected)
@ -155,10 +133,16 @@ class RadioInterfaceService : JobIntentService(), Logging {
broadcastReceivedFromRadio(this, p)
}
/// Attempt to read from the fromRadio mailbox, if data is found broadcast it to android apps
private fun doReadFromRadio() {
safe.asyncReadCharacteristic(fromRadio) {
logAssert(it.isSuccess) // FIXME, handle failure
}
}
override fun onCreate() {
super.onCreate()
// FIXME, the lifecycle is wrong for jobintentservice, change to a regular service
// FIXME, let user GUI select which device we are talking to
// Note: this call does no comms, it just creates the device object (even if the
@ -171,9 +155,24 @@ class RadioInterfaceService : JobIntentService(), Logging {
// comes in range (even if we made this connect call long ago when we got powered on)
// see https://stackoverflow.com/questions/40156699/which-correct-flag-of-autoconnect-in-connectgatt-of-ble for
// more info
// FIXME, can't use sync connect here - because it could take a LONG time
// FIXME, don't use sync api at all - because our operations are so simple and atomic
safe.connect(true)
// FIXME, broadcast connection lost/gained via broadcastConnecionChanged
safe.asyncConnect(true) { connRes ->
// This callback is invoked after we are connected
logAssert(connRes.isSuccess) // FIXME, instead just try to reconnect?
safe.asyncDiscoverServices { discRes ->
logAssert(discRes.isSuccess) // IXME, instead just try to reconnect?
val service = safe.gatt.services.find { it.uuid == BTM_SERVICE_UUID }!!
fromRadio = service.getCharacteristic(BTM_FROMRADIO_CHARACTER)
toRadio = service.getCharacteristic(BTM_FROMRADIO_CHARACTER)
fromNum = service.getCharacteristic(BTM_FROMNUM_CHARACTER)
doReadFromRadio()
}
}
sentPacketsLog = DebugLogFile(this, "sent_log.json")
}
@ -183,13 +182,13 @@ class RadioInterfaceService : JobIntentService(), Logging {
super.onDestroy()
}
override fun onHandleWork(intent: Intent) { // We have received work to do. The system or framework is already
// holding a wake lock for us at this point, so we can just go.
debug("Executing work: $intent")
when (intent.action) {
SEND_TORADIO_ACTION -> handleSendToRadio(intent.getByteArrayExtra(EXTRA_PAYLOAD)!!)
else -> TODO("Unhandled case")
}
override fun onBind(intent: Intent?): IBinder? {
return binder;
}
private val binder = object : IRadioInterfaceService.Stub() {
override fun sendToRadio(a: ByteArray) {
handleFromRadio(a)
}
}
}

Wyświetl plik

@ -133,7 +133,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
if (status != 0)
work.completion.resumeWithException(IOException("Bluetooth status=$status"))
else
work.completion.resume(Result.success(res) as Result<Nothing>) // FIXME, will this work?
work.completion.resume(Result.success(res) as Result<Nothing>)
}
/**