diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0f63a8e1..9e227dee 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -68,7 +68,6 @@
diff --git a/app/src/main/aidl/com/geeksville/mesh/IRadioInterfaceService.aidl b/app/src/main/aidl/com/geeksville/mesh/IRadioInterfaceService.aidl
new file mode 100644
index 00000000..626042c6
--- /dev/null
+++ b/app/src/main/aidl/com/geeksville/mesh/IRadioInterfaceService.aidl
@@ -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);
+}
diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
index 93fc0990..24c9a898 100644
--- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt
+++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
@@ -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() {
diff --git a/app/src/main/java/com/geeksville/mesh/MeshService.kt b/app/src/main/java/com/geeksville/mesh/MeshService.kt
index 727d66e5..98c8ee7d 100644
--- a/app/src/main/java/com/geeksville/mesh/MeshService.kt
+++ b/app/src/main/java/com/geeksville/mesh/MeshService.kt
@@ -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()
+ 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()
}
diff --git a/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt b/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt
index e46d809a..1223ef66 100644
--- a/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt
+++ b/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt
@@ -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)
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/geeksville/mesh/SafeBluetooth.kt b/app/src/main/java/com/geeksville/mesh/SafeBluetooth.kt
index a8c90a38..43d2f861 100644
--- a/app/src/main/java/com/geeksville/mesh/SafeBluetooth.kt
+++ b/app/src/main/java/com/geeksville/mesh/SafeBluetooth.kt
@@ -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) // FIXME, will this work?
+ work.completion.resume(Result.success(res) as Result)
}
/**