kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
new bluetooth work queue stuff seems to work
rodzic
6edc89e2aa
commit
6cebf063d7
|
@ -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" />
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Ładowanie…
Reference in New Issue