2020-01-24 17:05:55 -08:00
|
|
|
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
|
2020-01-24 17:05:55 -08:00
|
|
|
import com.geeksville.android.Logging
|
2020-01-24 20:35:42 -08:00
|
|
|
import com.google.protobuf.util.JsonFormat
|
2020-01-24 17:05:55 -08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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"
|
2020-01-25 11:40:13 -08:00
|
|
|
|
2020-01-24 17:05:55 -08:00
|
|
|
/**
|
|
|
|
|
* 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()
|
2020-01-25 11:40:13 -08:00
|
|
|
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 17:05:55 -08:00
|
|
|
}
|
|
|
|
|
|
2020-01-24 20:35:42 -08:00
|
|
|
lateinit var sentPacketsLog: DebugLogFile // inited in onCreate
|
2020-01-24 17:47:32 -08:00
|
|
|
|
2020-01-24 17:05:55 -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-25 11:40:13 -08:00
|
|
|
|
2020-01-24 20:35:42 -08:00
|
|
|
val json = jsonPrinter.print(proto).replace('\n', ' ')
|
2020-01-25 11:40:13 -08:00
|
|
|
info("TODO sending to radio: $json")
|
2020-01-24 20:35:42 -08:00
|
|
|
sentPacketsLog.log(json)
|
2020-01-25 11:40:13 -08:00
|
|
|
|
|
|
|
|
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())
|
|
|
|
|
}
|
2020-01-24 17:05:55 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-24 17:05:55 -08:00
|
|
|
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)!!)
|
2020-01-24 17:05:55 -08:00
|
|
|
else -> TODO("Unhandled case")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|