diff --git a/TODO.md b/TODO.md index 8ad40ed22..4efbb883f 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,7 @@ * use android service from Signal * DONE handle failures in onCharWrite, instead of logAssert - because they can happen if device goes away -* explictly broadcast towards signal https://developer.android.com/guide/components/broadcasts +* DONE explictly broadcast towards signal https://developer.android.com/guide/components/broadcasts * make test implementation of android service (doesn't use bluetooth) * clean up sw update code in device side * DONE add broadcasters for use by signal (node changes and packet received) @@ -10,7 +10,7 @@ * add real messaging code/protobufs * use https://codelabs.developers.google.com/codelabs/jetpack-compose-basics/#4 to show service state * connect to bluetooth device automatically using minimum power -* have signal declare receivers: https://developer.android.com/guide/components/broadcasts#manifest-declared-receivers +* DONE have signal declare receivers: https://developer.android.com/guide/components/broadcasts#manifest-declared-receivers * fix BT device scanning * call crashlytics from exceptionReporter!!! currently not logging failures caught there diff --git a/app/src/main/aidl/com/geeksville/mesh/IMeshService.aidl b/app/src/main/aidl/com/geeksville/mesh/IMeshService.aidl index a07ccf409..4e9c112ae 100644 --- a/app/src/main/aidl/com/geeksville/mesh/IMeshService.aidl +++ b/app/src/main/aidl/com/geeksville/mesh/IMeshService.aidl @@ -7,6 +7,9 @@ package com.geeksville.mesh; * Note - these calls might throw RemoteException to indicate mesh error states */ interface IMeshService { + /// Tell the service where to send its broadcasts of received packets + void subscribeReceiver(String packageName, String receiverName); + /** * Set the ID info for this node diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index b85cfdb1b..93fc09905 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -157,7 +157,7 @@ class MainActivity : AppCompatActivity(), Logging { override fun onServiceConnected(name: ComponentName, service: IBinder) { val m = IMeshService.Stub.asInterface(service) meshService = m - + // Do some test operations m.setOwner("+16508675309", "Kevin Xter", "kx") val testPayload = "hello world".toByteArray() diff --git a/app/src/main/java/com/geeksville/mesh/MeshService.kt b/app/src/main/java/com/geeksville/mesh/MeshService.kt index 57396f691..2bda3418e 100644 --- a/app/src/main/java/com/geeksville/mesh/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/MeshService.kt @@ -1,10 +1,13 @@ package com.geeksville.mesh +import android.app.Activity +import android.app.ActivityManager import android.app.Service import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.os.Binder import android.os.IBinder import com.geeksville.android.Logging import com.geeksville.mesh.MeshProtos.MeshPacket @@ -14,6 +17,7 @@ import com.geeksville.util.toRemoteExceptions import com.google.protobuf.ByteString import java.nio.charset.Charset + /** * Handles all the communication with android apps. Also keeps an internal model * of the network state. @@ -35,6 +39,10 @@ class MeshService : Service(), Logging { private const val NODE_NUM_NO_MESH = -1 } + + /// A mapping of receiver class name to package name - used for explicit broadcasts + private val clientPackages = mutableMapOf() + /* see com.geeksville.mesh broadcast intents // RECEIVED_OPAQUE for data received from other nodes @@ -42,6 +50,13 @@ class MeshService : Service(), Logging { // CONNECTION_CHANGED for losing/gaining connection to the packet radio */ + private fun explicitBroadcast(intent: Intent) { + clientPackages.forEach { + intent.setClassName(it.value, it.key) + sendBroadcast(intent) + } + } + /** * The RECEIVED_OPAQUE: * Payload will be the raw bytes which were contained within a MeshPacket.Opaque field @@ -53,14 +68,14 @@ class MeshService : Service(), Logging { intent.putExtra(EXTRA_SENDER, senderId) intent.putExtra(EXTRA_PAYLOAD, payload) intent.putExtra(EXTRA_TYP, typ) - sendBroadcast(intent) + explicitBroadcast(intent) } private fun broadcastNodeChange(nodeId: String, isOnline: Boolean) { val intent = Intent("$prefix.NODE_CHANGE") intent.putExtra(EXTRA_ID, nodeId) intent.putExtra(EXTRA_ONLINE, isOnline) - sendBroadcast(intent) + explicitBroadcast(intent) } /// Send a command/packet to our radio @@ -68,8 +83,25 @@ class MeshService : Service(), Logging { RadioInterfaceService.sendToRadio(this, p.build().toByteArray()) } - override fun onBind(intent: Intent): IBinder { - // Return the interface + /** + * We track everyone who has bound to us, so when we can explicitly broadcast to them + * per new restrictions in android api 26 https://developer.android.com/guide/components/broadcasts#receiving-broadcasts + */ + private fun recordNewClient() { + val pid = Binder.getCallingPid() + + val activityManager = getSystemService(Activity.ACTIVITY_SERVICE) as ActivityManager + val procs = activityManager.runningAppProcesses.filter { + it.pid == pid + } + val packages = procs.flatMap { + it.pkgList.asList() + } + + clientPackages.addAll(packages) + } + + override fun onBind(intent: Intent?): IBinder? { return binder } @@ -289,6 +321,10 @@ class MeshService : Service(), Logging { private val binder = object : IMeshService.Stub() { // Note: bound methods don't get properly exception caught/logged, so do that with a wrapper // per https://blog.classycode.com/dealing-with-exceptions-in-aidl-9ba904c6d63 + override fun subscribeReceiver(packageName: String, receiverName: String) = + toRemoteExceptions { + clientPackages[receiverName] = packageName + } override fun setOwner(myId: String, longName: String, shortName: String) = toRemoteExceptions {