diff --git a/README.md b/README.md
index 1fbdc5287..c5e52b229 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Questions? kevinh@geeksville.com
Once this project is public, I'll happily let collaborators have access to the crash logs/analytics.
* analytics is currently on, before beta is over I'll make it optional
-* on dev devices "adb shell setprop debug.firebase.analytics.app com.geeeksville.mesh"
+* on dev devices "adb shell setprop debug.firebase.analytics.app com.geeksville.mesh"
* To see analytics: https://console.firebase.google.com/u/0/project/meshutil/analytics/app/android:com.geeksville.mesh/overview
* To see crash logs: https://console.firebase.google.com/u/0/project/meshutil/crashlytics/app/android:com.geeksville.mesh/issues?state=open&time=last-seven-days&type=crash
diff --git a/app/src/androidTest/java/com/geeksville/mesh/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/geeksville/mesh/ExampleInstrumentedTest.kt
index 64799310d..2ae926b86 100644
--- a/app/src/androidTest/java/com/geeksville/mesh/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/com/geeksville/mesh/ExampleInstrumentedTest.kt
@@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.geeksville.com.geeeksville.mesh", appContext.packageName)
+ assertEquals("com.geeksville.com.geeksville.mesh", appContext.packageName)
}
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9643c683a..782380cc9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -16,15 +16,13 @@
This permission is required to allow the application to send
events and properties to Mixpanel.
-->
-
+
-
+
-
@@ -49,11 +48,18 @@
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />
+
+
+
+
-
+
diff --git a/app/src/main/aidl/com/geeksville/mesh/IMeshService.aidl b/app/src/main/aidl/com/geeksville/mesh/IMeshService.aidl
index b7025bd72..68687fb3d 100644
--- a/app/src/main/aidl/com/geeksville/mesh/IMeshService.aidl
+++ b/app/src/main/aidl/com/geeksville/mesh/IMeshService.aidl
@@ -1,4 +1,4 @@
-// com.geeksville.com.geeeksville.mesh.IMeshService.aidl
+// com.geeksville.mesh.IMeshService.aidl
package com.geeksville.mesh;
// Declare any non-default types here with import statements
@@ -24,7 +24,7 @@ interface IMeshService {
*/
boolean isConnected();
- // see com.geeksville.com.geeeksville.mesh broadcast intents
+ // see com.geeksville.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
diff --git a/app/src/main/java/com/geeksville/mesh/MeshService.kt b/app/src/main/java/com/geeksville/mesh/MeshService.kt
index 42419a2d0..9f540f9c2 100644
--- a/app/src/main/java/com/geeksville/mesh/MeshService.kt
+++ b/app/src/main/java/com/geeksville/mesh/MeshService.kt
@@ -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")
diff --git a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt b/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
index 116d370e8..5f418d76d 100644
--- a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
+++ b/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
@@ -2,4 +2,7 @@ package com.geeksville.mesh
import com.geeksville.android.GeeksvilleApplication
-class MeshUtilApplication : GeeksvilleApplication(null, "58e72ccc361883ea502510baa46580e3")
\ No newline at end of file
+const val prefix = "com.geeksville.mesh"
+
+class MeshUtilApplication : GeeksvilleApplication(null, "58e72ccc361883ea502510baa46580e3") {
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt b/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt
new file mode 100644
index 000000000..6257bb1ee
--- /dev/null
+++ b/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt
@@ -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")
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/geeksville/mesh/SoftwareUpdateService.kt b/app/src/main/java/com/geeksville/mesh/SoftwareUpdateService.kt
index 7c823c68b..18568583a 100644
--- a/app/src/main/java/com/geeksville/mesh/SoftwareUpdateService.kt
+++ b/app/src/main/java/com/geeksville/mesh/SoftwareUpdateService.kt
@@ -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
diff --git a/app/src/main/proto/mesh.proto b/app/src/main/proto/mesh.proto
index a9859d607..b0b3c380c 100644
--- a/app/src/main/proto/mesh.proto
+++ b/app/src/main/proto/mesh.proto
@@ -3,7 +3,7 @@ syntax = "proto3";
package mesh;
-option java_package = "com.geeksville.com.geeeksville.mesh";
+option java_package = "com.geeksville.mesh";
option java_outer_classname = "MeshProtos";
/**
@@ -42,40 +42,40 @@ node number, or 0xff for broadcast.
// a gps position
message Position {
- double latitude = 1;
- double longitude = 2;
- int32 altitude = 3;
+ double latitude = 1;
+ double longitude = 2;
+ int32 altitude = 3;
}
// Times are typically not sent over the mesh, but they will be added to any Packet (chain of SubPacket)
// sent to the phone (so the phone can know exact time of reception)
message Time {
- uint64 msecs = 1; // msecs since 1970
+ uint64 msecs = 1; // msecs since 1970
}
// A message sent from a device outside of the mesh, in a form the mesh does not understand
// i.e. a Signal app level message.
message Opaque {
- bytes payload = 1;
+ bytes payload = 1;
}
// a simple text message, which even the little micros in the mesh can understand and show on their screen
message Text {
- string text = 1;
+ string text = 1;
}
// Sent from the phone over bluetooth to set the user id for the owner of this node.
// Also sent from nodes to each other when a new node signs on (so all clients can have this info)
message User {
- string id = 1; // a globally unique ID string for this user. In the case of Signal that would mean +16504442323
- string long_name = 2; // A full name for this user, i.e. "Kevin Hester"
- string short_name = 3; // A VERY short name, ideally two characters. Suitable for a tiny OLED screen
+ string id = 1; // a globally unique ID string for this user. In the case of Signal that would mean +16504442323
+ string long_name = 2; // A full name for this user, i.e. "Kevin Hester"
+ string short_name = 3; // A VERY short name, ideally two characters. Suitable for a tiny OLED screen
}
// Broadcast when a newly powered mesh node wants to find a node num it can use (see document for more
// details)
message WantNodeNum {
- // No payload, just its existence is sufficent (desired node num will be in the from field)
+ // No payload, just its existence is sufficent (desired node num will be in the from field)
}
// Sent to a node which has requested a nodenum when it is told it can't have it
@@ -84,35 +84,34 @@ message DenyNodeNum {
// A single packet might have a series of SubPacket included
message SubPacket {
- oneof variant {
- Position position = 1;
- Time time = 2;
- Text text = 3;
- Opaque opaque = 4;
- User user = 5;
- WantNodeNum want_node = 6;
- DenyNodeNum deny_node = 7;
- }
+ oneof variant {
+ Position position = 1;
+ Time time = 2;
+ Text text = 3;
+ Opaque opaque = 4;
+ User user = 5;
+ WantNodeNum want_node = 6;
+ DenyNodeNum deny_node = 7;
+ }
}
-
// A packet sent over our mesh.
// NOTE: this raw payload does not include the from and to addresses, which are stripped off
// and passed into the mesh library code separately.
message MeshPayload {
- repeated SubPacket subPackets = 3;
+ repeated SubPacket subPackets = 3;
}
// A full packet sent/received over the mesh
message MeshPacket {
- int32 from = 1;
- int32 to = 2;
- MeshPayload payload = 3;
+ int32 from = 1;
+ int32 to = 2;
+ MeshPayload payload = 3;
}
// Full settings (center freq, spread factor, preshared secret key etc...) needed to configure a radio
message RadioConfig {
- // FIXME
+ // FIXME
}
/**
@@ -139,11 +138,11 @@ SET_CONFIG (switches device to a new set of radio params and preshared key, drop
// Full information about a node on the mesh
message NodeInfo {
- int32 num = 1; // the node number
- User user = 2;
- int32 battery_level = 3; // 0-100
- Position position = 4;
- Time last_seen = 5;
+ int32 num = 1; // the node number
+ User user = 2;
+ int32 battery_level = 3; // 0-100
+ Position position = 4;
+ Time last_seen = 5;
}
// packets from the radio to the phone will appear on the fromRadio characteristic. It will support
@@ -151,27 +150,27 @@ message NodeInfo {
// it will sit in that descriptor until consumed by the phone, at which point the next item in the FIFO
// will be populated. FIXME
message FromRadio {
- oneof variant {
- MeshPacket packet = 1;
- NodeInfo node_info = 2;
- }
+ oneof variant {
+ MeshPacket packet = 1;
+ NodeInfo node_info = 2;
+ }
}
// packets/commands to the radio will be written (reliably) to the toRadio characteristic. Once the
// write completes the phone can assume it is handled.
message ToRadio {
- // If sent to the radio, the radio will send the phone its full node DB (NodeInfo records)
- // Used to populate network info the first time the phone connects to the radio
- message WantNodes {
- // Empty
- }
+ // If sent to the radio, the radio will send the phone its full node DB (NodeInfo records)
+ // Used to populate network info the first time the phone connects to the radio
+ message WantNodes {
+ // Empty
+ }
- oneof variant {
- MeshPacket packet = 1; // send this packet on the mesh
- RadioConfig set_radio = 2; // set the radio provisioning for this node
- User set_owner = 3; // Set the owner for this node
- WantNodes want_nodes = 4; // phone wants radio to send full node db to the phone
- }
+ oneof variant {
+ MeshPacket packet = 1; // send this packet on the mesh
+ RadioConfig set_radio = 2; // set the radio provisioning for this node
+ User set_owner = 3; // Set the owner for this node
+ WantNodes want_nodes = 4; // phone wants radio to send full node db to the phone
+ }
}