Meshtastic-Android/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt

157 lines
7 KiB
Kotlin
Raw Normal View History

package com.geeksville.mesh
import android.content.Context
import android.content.Intent
import androidx.core.app.JobIntentService
2020-01-24 17:47:32 -08:00
import com.geeksville.android.DebugLogFile
import com.geeksville.android.Logging
2020-01-24 20:35:42 -08:00
import com.google.protobuf.util.JsonFormat
/**
* 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)
}
2020-01-24 20:35:42 -08:00
// for debug logging only
private val jsonPrinter = JsonFormat.printer()
private val jsonParser = JsonFormat.parser()
/**
* When simulating we parse these MeshPackets as if they arrived at startup
* Send broadcast them after we receive a ToRadio.WantNodes message.
*
* Our fake net has three nodes
*
* +16508675309, nodenum 9 - our node
* +16508675310, nodenum 10 - some other node, name Bob One/BO
* (eventually) +16508675311, nodenum 11 - some other node
*/
/*
2020-01-25 11:02:05.279 1162-1280/com.geeksville.mesh D/com.geeksville.mesh.RadioInterfaceService: Executing work: Intent { act=com.geeksville.mesh.SEND_TORADIO (has extras) }
2020-01-25 11:02:05.282 1162-1273/com.geeksville.mesh D/EGL_emulation: eglMakeCurrent: 0xebb2b500: ver 2 0 (tinfo 0xc9748bc0)
2020-01-25 11:02:05.449 1162-1280/com.geeksville.mesh I/com.geeksville.mesh.RadioInterfaceService: TODO sending to radio: { "wantNodes": { } }
2020-01-25 11:02:05.452 1162-1280/com.geeksville.mesh D/com.geeksville.mesh.RadioInterfaceService: Executing work: Intent { act=com.geeksville.mesh.SEND_TORADIO (has extras) }
2020-01-25 11:02:05.479 1162-1280/com.geeksville.mesh I/com.geeksville.mesh.RadioInterfaceService: TODO sending to radio: { "setOwner": { "id": "+16508675309", "longName": "Kevin Xter", "shortName": "kx" } }
2020-01-25 11:02:05.480 1162-1280/com.geeksville.mesh D/com.geeksville.mesh.RadioInterfaceService: Executing work: Intent { act=com.geeksville.mesh.SEND_TORADIO (has extras) }
2020-01-25 11:02:05.504 1162-1280/com.geeksville.mesh I/com.geeksville.mesh.RadioInterfaceService: TODO sending to radio: { "packet": { "from": -1, "to": 10, "payload": { "subPackets": [{ "data": { "payload": "aGVsbG8gd29ybGQ=" } }] } } }
2020-01-25 11:02:05.505 1162-1280/com.geeksville.mesh D/com.geeksville.mesh.RadioInterfaceService: Executing work: Intent { act=com.geeksville.mesh.SEND_TORADIO (has extras) }
2020-01-25 11:02:05.510 1162-1280/com.geeksville.mesh I/com.geeksville.mesh.RadioInterfaceService: TODO sending to radio: { "packet": { "from": -1, "to": 10, "payload": { "subPackets": [{ "data": { "payload": "aGVsbG8gd29ybGQ=" } }] } } }
2020-01-25 11:02:08.232 1162-1273/com.geeksville.mesh D/EGL_emulation: eglMakeCurrent: 0xebb2b500: ver 2 0 (tinfo 0xc9748bc0)
*/
val simInitPackets =
arrayOf(
""" { "from": 10, "to": 9, "payload": { "subPackets": [{ "user": { "id": "+16508675310", "longName": "Bob One", "shortName": "BO" }}]}} """,
""" { "from": 10, "to": 9, "payload": { "subPackets": [{ "data": { "payload": "aGVsbG8gd29ybGQ=", "typ": 0 }}]}} """, // SIGNAL_OPAQUE
""" { "from": 10, "to": 9, "payload": { "subPackets": [{ "data": { "payload": "aGVsbG8gd29ybGQ=", "typ": 1 }}]}} """, // CLEAR_TEXT
""" { "from": 10, "to": 9, "payload": { "subPackets": [{ "data": { "payload": "", "typ": 2 }}]}} """ // CLEAR_READACK
)
// FIXME, move into a subclass?
val isSimulating = true
}
2020-01-24 20:35:42 -08:00
lateinit var sentPacketsLog: DebugLogFile // inited in onCreate
2020-01-24 17:47:32 -08:00
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
2020-01-25 10:00:57 -08:00
private fun handleSendToRadio(p: ByteArray) {
2020-01-24 17:47:32 -08:00
// For debugging/logging purposes ONLY we convert back into a protobuf for readability
val proto = MeshProtos.ToRadio.parseFrom(p)
2020-01-24 20:35:42 -08:00
val json = jsonPrinter.print(proto).replace('\n', ' ')
info("TODO sending to radio: $json")
2020-01-24 20:35:42 -08:00
sentPacketsLog.log(json)
if (isSimulating)
simInitPackets.forEach { json ->
val fromRadio = MeshProtos.FromRadio.newBuilder().apply {
packet = MeshProtos.MeshPacket.newBuilder().apply {
jsonParser.merge(json, this)
}.build()
}.build()
broadcastReceivedFromRadio(fromRadio.toByteArray())
}
}
// Handle an incoming packet from the radio, broadcasts it as an android intent
private fun handleFromRadio(p: ByteArray) {
broadcastReceivedFromRadio(p)
}
2020-01-24 20:35:42 -08:00
override fun onCreate() {
super.onCreate()
sentPacketsLog = DebugLogFile(this, "sent_log.json")
}
2020-01-24 17:47:32 -08:00
override fun onDestroy() {
sentPacketsLog.close()
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) {
2020-01-25 10:00:57 -08:00
SEND_TORADIO_ACTION -> handleSendToRadio(intent.getByteArrayExtra(EXTRA_PAYLOAD)!!)
else -> TODO("Unhandled case")
}
}
}