add beginnings of radio interface service

This commit is contained in:
geeksville 2020-01-24 17:05:55 -08:00
parent 5c9696588e
commit 559795b796
9 changed files with 203 additions and 90 deletions

View file

@ -1,42 +1,45 @@
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.os.IBinder
import com.geeksville.android.Logging
/**
* Handles all the communication with android apps. Also keeps an internal model
* of the network state.
*
* Note: this service will go away once all clients are unbound from it.
*/
class MeshService : Service(), Logging {
companion object {
const val prefix = "com.geeksville.mesh"
}
/*
see com.geeksville.mesh broadcast intents
// RECEIVED_OPAQUE for data received from other nodes
// NODE_CHANGE for new IDs appearing or disappearing
// CONNECTION_CHANGED for losing/gaining connection to the packet radio
*/
/**
* The RECEIVED_OPAQUE:
* Payload will be the raw bytes which were contained within a MeshPacket.Opaque field
* Sender will be a user ID string
*/
fun broadcastReceivedOpaque(senderId: String, payload: ByteArray) {
val intent = Intent("$prefix.RECEIVED_OPAQUE")
intent.putExtra("$prefix.Sender", senderId)
intent.putExtra("$prefix.Payload", payload)
intent.putExtra(EXTRA_SENDER, senderId)
intent.putExtra(EXTRA_PAYLOAD, payload)
sendBroadcast(intent)
}
fun broadcastNodeChange(nodeId: String, isOnline: Boolean) {
val intent = Intent("$prefix.NODE_CHANGE")
intent.putExtra("$prefix.Id", nodeId)
intent.putExtra("$prefix.Online", isOnline)
sendBroadcast(intent)
}
fun broadcastConnectionChanged(isConnected: Boolean) {
val intent = Intent("$prefix.CONNECTION_CHANGED")
intent.putExtra("$prefix.Connected", isConnected)
intent.putExtra(EXTRA_ID, nodeId)
intent.putExtra(EXTRA_ONLINE, isOnline)
sendBroadcast(intent)
}
@ -45,6 +48,30 @@ class MeshService : Service(), Logging {
return binder
}
override fun onCreate() {
super.onCreate()
val filter = IntentFilter(RadioInterfaceService.RECEIVE_FROMRADIO_ACTION)
registerReceiver(radioInterfaceReceiver, filter)
}
override fun onDestroy() {
unregisterReceiver(radioInterfaceReceiver)
super.onDestroy()
}
/**
* Receives messages from our BT radio service and processes them to update our model
* and send to clients as needed.
*/
private val radioInterfaceReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val proto = MeshProtos.FromRadio.parseFrom(intent.getByteArrayExtra(EXTRA_PAYLOAD)!!)
TODO("FIXME - update model and send messages as needed")
}
}
private val binder = object : IMeshService.Stub() {
override fun setOwner(myId: String, longName: String, shortName: String) {
error("TODO setOwner $myId : $longName : $shortName")

View file

@ -2,4 +2,7 @@ package com.geeksville.mesh
import com.geeksville.android.GeeksvilleApplication
class MeshUtilApplication : GeeksvilleApplication(null, "58e72ccc361883ea502510baa46580e3")
const val prefix = "com.geeksville.mesh"
class MeshUtilApplication : GeeksvilleApplication(null, "58e72ccc361883ea502510baa46580e3") {
}

View file

@ -0,0 +1,93 @@
package com.geeksville.mesh
import android.content.Context
import android.content.Intent
import androidx.core.app.JobIntentService
import com.geeksville.android.Logging
const val EXTRA_CONNECTED = "$prefix.Connected"
const val EXTRA_PAYLOAD = "$prefix.Payload"
const val EXTRA_SENDER = "$prefix.Sender"
const val EXTRA_ID = "$prefix.Id"
const val EXTRA_ONLINE = "$prefix.Online"
/**
* Handles the bluetooth link with a mesh radio device. Does not cache any device state,
* just does bluetooth comms etc...
*
* This service is not exposed outside of this process.
*
* 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 {
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
*/
const val RECEIVE_FROMRADIO_ACTION = "$prefix.RECEIVE_FROMRADIO"
/**
* 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)
}
}
private fun broadcastReceivedFromRadio(payload: ByteArray) {
val intent = Intent(RECEIVE_FROMRADIO_ACTION)
intent.putExtra("$prefix.Payload", payload)
sendBroadcast(intent)
}
fun broadcastConnectionChanged(isConnected: Boolean) {
val intent = Intent("$prefix.CONNECTION_CHANGED")
intent.putExtra(EXTRA_CONNECTED, isConnected)
sendBroadcast(intent)
}
/// Send a packet/command out the radio link
private fun sendToRadio(p: ByteArray) {
info("Simulating sending to radio size=$p.size")
}
// Handle an incoming packet from the radio, broadcasts it as an android intent
private fun handleFromRadio(p: ByteArray) {
broadcastReceivedFromRadio(p)
}
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 -> sendToRadio(intent.getByteArrayExtra(EXTRA_PAYLOAD)!!)
else -> TODO("Unhandled case")
}
}
}

View file

@ -10,10 +10,7 @@ import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.ParcelUuid
import android.os.SystemClock
import android.widget.Toast
import androidx.core.app.JobIntentService
import com.geeksville.android.Logging
import java.util.*
@ -184,19 +181,7 @@ class SoftwareUpdateService : JobIntentService(), Logging {
connectToTestDevice() // FIXME, pass in as an intent arg instead
startUpdate()
}
else -> logAssert(false)
}
debug(
"Completed service @ " + SystemClock.elapsedRealtime()
)
}
val mHandler = Handler()
// Helper for showing tests
fun toast(text: CharSequence?) {
mHandler.post {
Toast.makeText(this@SoftwareUpdateService, text, Toast.LENGTH_SHORT).show()
else -> TODO("Unhandled case")
}
}
@ -204,10 +189,10 @@ class SoftwareUpdateService : JobIntentService(), Logging {
/**
* Unique job ID for this service. Must be the same for all work.
*/
const val JOB_ID = 1000
private const val JOB_ID = 1000
val scanDevicesIntent = Intent("com.geeksville.com.geeeksville.mesh.SCAN_DEVICES")
val startUpdateIntent = Intent("com.geeksville.com.geeeksville.mesh.START_UPDATE")
val scanDevicesIntent = Intent("$prefix.SCAN_DEVICES")
val startUpdateIntent = Intent("$prefix.START_UPDATE")
private const val SCAN_PERIOD: Long = 10000