mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-03-29 19:23:30 -04:00
WIP - doesn't yet build, but changing to set the device addresses the correct way
This commit is contained in:
@@ -57,8 +57,14 @@ interface IMeshService {
|
||||
*/
|
||||
String connectionState();
|
||||
|
||||
/// If a macaddress we will try to talk to our device, if null we will be idle.
|
||||
/// Users should not call this directly, only used internally by the MeshUtil activity
|
||||
void setDeviceAddress(String deviceAddr);
|
||||
|
||||
// see com.geeksville.com.geeksville.mesh broadcast intents
|
||||
// RECEIVED_OPAQUE for data received from other nodes. payload will contain a DataPacket
|
||||
// NODE_CHANGE for new IDs appearing or disappearing
|
||||
// CONNECTION_CHANGED for losing/gaining connection to the packet radio
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ interface IRadioInterfaceService {
|
||||
byte []readOwner();
|
||||
void writeOwner(in byte [] owner);
|
||||
|
||||
/// If true we will try to talk to our device, if false we will shutdown. Useful during software update.
|
||||
void enableLink(boolean enable);
|
||||
/// If a macaddress we will try to talk to our device, if null we will be idle.
|
||||
/// Users should not call this directly, called only by MeshService
|
||||
void setDeviceAddress(String deviceAddr);
|
||||
}
|
||||
|
||||
@@ -436,7 +436,9 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||
}
|
||||
|
||||
// ... Continue interacting with the paired device.
|
||||
RadioInterfaceService.setBondedDeviceAddress(this, device.address)
|
||||
model.meshService?.let { service ->
|
||||
service.setDeviceAddress(device.address)
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
@@ -636,7 +638,7 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||
}
|
||||
}
|
||||
|
||||
fun bindMeshService() {
|
||||
private fun bindMeshService() {
|
||||
debug("Binding to mesh service!")
|
||||
// we bind using the well known name, to make sure 3rd party apps could also
|
||||
if (model.meshService != null)
|
||||
@@ -648,7 +650,7 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||
}
|
||||
}
|
||||
|
||||
fun unbindMeshService() {
|
||||
private fun unbindMeshService() {
|
||||
// If we have received the service, and hence registered with
|
||||
// it, then now is the time to unregister.
|
||||
// if we never connected, do nothing
|
||||
|
||||
@@ -37,7 +37,7 @@ import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
|
||||
class RadioNotConnectedException() : Exception("Not connected to radio")
|
||||
class RadioNotConnectedException(message: String = "Not connected to radio") : Exception(message)
|
||||
|
||||
|
||||
private val errorHandler = CoroutineExceptionHandler { _, exception ->
|
||||
@@ -73,35 +73,30 @@ class MeshService : Service(), Logging {
|
||||
/// Helper function to start running our service, returns the intent used to reach it
|
||||
/// or null if the service could not be started (no bluetooth or no bonded device set)
|
||||
fun startService(context: Context): Intent? {
|
||||
if (RadioInterfaceService.getBondedDeviceAddress(context) == null) {
|
||||
warn("No mesh radio is bonded, not starting service")
|
||||
return null
|
||||
} else {
|
||||
// bind to our service using the same mechanism an external client would use (for testing coverage)
|
||||
// The following would work for us, but not external users
|
||||
//val intent = Intent(this, MeshService::class.java)
|
||||
//intent.action = IMeshService::class.java.name
|
||||
val intent = Intent()
|
||||
intent.setClassName(
|
||||
"com.geeksville.mesh",
|
||||
"com.geeksville.mesh.service.MeshService"
|
||||
)
|
||||
// bind to our service using the same mechanism an external client would use (for testing coverage)
|
||||
// The following would work for us, but not external users
|
||||
//val intent = Intent(this, MeshService::class.java)
|
||||
//intent.action = IMeshService::class.java.name
|
||||
val intent = Intent()
|
||||
intent.setClassName(
|
||||
"com.geeksville.mesh",
|
||||
"com.geeksville.mesh.service.MeshService"
|
||||
)
|
||||
|
||||
// Before binding we want to explicitly create - so the service stays alive forever (so it can keep
|
||||
// listening for the bluetooth packets arriving from the radio. And when they arrive forward them
|
||||
// to Signal or whatever.
|
||||
// Before binding we want to explicitly create - so the service stays alive forever (so it can keep
|
||||
// listening for the bluetooth packets arriving from the radio. And when they arrive forward them
|
||||
// to Signal or whatever.
|
||||
|
||||
logAssert(
|
||||
(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// we have some samsung devices failing with https://issuetracker.google.com/issues/76112072#comment56 not sure what the fix is yet
|
||||
context.startForegroundService(intent)
|
||||
} else {
|
||||
context.startService(intent)
|
||||
}) != null
|
||||
)
|
||||
logAssert(
|
||||
(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// we have some samsung devices failing with https://issuetracker.google.com/issues/76112072#comment56 not sure what the fix is yet
|
||||
context.startForegroundService(intent)
|
||||
} else {
|
||||
context.startService(intent)
|
||||
}) != null
|
||||
)
|
||||
|
||||
return intent
|
||||
}
|
||||
return intent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1069,6 +1064,12 @@ class MeshService : Service(), Logging {
|
||||
}
|
||||
|
||||
private val binder = object : IMeshService.Stub() {
|
||||
|
||||
override fun setDeviceAddress(deviceAddr: String?) {
|
||||
debug("Passing through device change to radio service: $deviceAddr")
|
||||
connectedRadio.setDeviceAddress(deviceAddr)
|
||||
}
|
||||
|
||||
// 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) =
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
||||
import android.app.Service
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import android.bluetooth.BluetoothGattService
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.companion.CompanionDeviceManager
|
||||
import android.content.Context
|
||||
@@ -184,52 +185,6 @@ class RadioInterfaceService : Service(), Logging {
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun setBondedDeviceAddress(context: Context, addr: String?) {
|
||||
// Record that this use has configured a radio
|
||||
GeeksvilleApplication.analytics.track(
|
||||
"mesh_bond"
|
||||
)
|
||||
|
||||
debug("Setting bonded device to $addr")
|
||||
if (hasCompanionDeviceApi((context))) {
|
||||
// We only keep an association to one device at a time...
|
||||
if (addr != null) {
|
||||
val deviceManager = context.getSystemService(CompanionDeviceManager::class.java)
|
||||
|
||||
deviceManager.associations.forEach { old ->
|
||||
if (addr != old) {
|
||||
debug("Forgetting old BLE association $old")
|
||||
deviceManager.disassociate(old)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getPrefs(context).edit(commit = true) {
|
||||
if (addr == null)
|
||||
this.remove(DEVADDR_KEY)
|
||||
else
|
||||
putString(DEVADDR_KEY, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Force the service to reconnect
|
||||
val s = runningService
|
||||
if (s != null) {
|
||||
// Ignore any errors that happen while closing old device
|
||||
ignoreException {
|
||||
info("shutting down old service")
|
||||
s.setEnabled(false) // nasty, needed to force the next setEnabled call to reconnect
|
||||
}
|
||||
info("Setting enable on the running radio service")
|
||||
s.setEnabled(addr != null)
|
||||
}
|
||||
if (addr != null) {
|
||||
info("We have a device addr now, starting mesh service")
|
||||
MeshService.startService(context)
|
||||
}
|
||||
}
|
||||
|
||||
/// Can we use the modern BLE scan API?
|
||||
fun hasCompanionDeviceApi(context: Context): Boolean = false /* ALAS - not ready for production yet
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
@@ -250,8 +205,11 @@ class RadioInterfaceService : Service(), Logging {
|
||||
/// Our BLE device
|
||||
val device get() = safe!!.gatt!!
|
||||
|
||||
/// Our service
|
||||
val service get() = device.getService(BTM_SERVICE_UUID)
|
||||
/// Our service - note - it is possible to get back a null response for getService if the device services haven't yet been found
|
||||
val service
|
||||
get(): BluetoothGattService = device.getService(BTM_SERVICE_UUID)
|
||||
?: throw RadioNotConnectedException("BLE service not found")
|
||||
|
||||
//.services.find { it.uuid == BTM_SERVICE_UUID }!!
|
||||
|
||||
private lateinit var fromNum: BluetoothGattCharacteristic
|
||||
@@ -328,6 +286,49 @@ class RadioInterfaceService : Service(), Logging {
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun setBondedDeviceAddress(addr: String?) {
|
||||
// Record that this use has configured a radio
|
||||
GeeksvilleApplication.analytics.track(
|
||||
"mesh_bond"
|
||||
)
|
||||
|
||||
// Ignore any errors that happen while closing old device
|
||||
ignoreException {
|
||||
Companion.info("shutting down old service")
|
||||
setEnabled(false) // nasty, needed to force the next setEnabled call to reconnect
|
||||
}
|
||||
|
||||
debug("Setting bonded device to $addr")
|
||||
if (hasCompanionDeviceApi(this)) {
|
||||
// We only keep an association to one device at a time...
|
||||
if (addr != null) {
|
||||
val deviceManager = getSystemService(CompanionDeviceManager::class.java)
|
||||
|
||||
deviceManager.associations.forEach { old ->
|
||||
if (addr != old) {
|
||||
Companion.debug("Forgetting old BLE association $old")
|
||||
deviceManager.disassociate(old)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getPrefs(this).edit(commit = true) {
|
||||
if (addr == null)
|
||||
this.remove(DEVADDR_KEY)
|
||||
else
|
||||
putString(DEVADDR_KEY, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Force the service to reconnect
|
||||
if (addr != null) {
|
||||
info("Setting enable on the running radio service")
|
||||
setEnabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun onDisconnect() {
|
||||
broadcastConnectionChanged(false)
|
||||
isConnected = false
|
||||
@@ -418,7 +419,7 @@ class RadioInterfaceService : Service(), Logging {
|
||||
} else {
|
||||
val address = getBondedDeviceAddress(this)
|
||||
if (address == null)
|
||||
errormsg("No bonded mesh radio, can't create service")
|
||||
errormsg("No bonded mesh radio, can't start service")
|
||||
else {
|
||||
// Note: this call does no comms, it just creates the device object (even if the
|
||||
// device is off/not connected)
|
||||
@@ -518,8 +519,9 @@ class RadioInterfaceService : Service(), Logging {
|
||||
}
|
||||
|
||||
private val binder = object : IRadioInterfaceService.Stub() {
|
||||
override fun enableLink(enable: Boolean) = toRemoteExceptions {
|
||||
setEnabled(enable)
|
||||
|
||||
override fun setDeviceAddress(deviceAddr: String?) = toRemoteExceptions {
|
||||
setBondedDeviceAddress(deviceAddr)
|
||||
}
|
||||
|
||||
// A write of any size to nodeinfo means restart reading
|
||||
|
||||
@@ -36,15 +36,18 @@ object SLogging : Logging {}
|
||||
|
||||
/// Change to a new macaddr selection, updating GUI and radio
|
||||
fun changeDeviceSelection(context: MainActivity, newAddr: String?) {
|
||||
RadioInterfaceService.setBondedDeviceAddress(context, newAddr)
|
||||
model.meshService?.let { service ->
|
||||
service.setDeviceAddress(context, newAddr)
|
||||
|
||||
// Super ugly hack. we force the activity to reconnect FIXME, find a cleaner way
|
||||
context.unbindMeshService()
|
||||
context.bindMeshService()
|
||||
}
|
||||
}
|
||||
|
||||
/// Show the UI asking the user to bond with a device, call changeSelection() if/when bonding completes
|
||||
private fun requestBonding(activity: MainActivity, device: BluetoothDevice, onSuccess: () -> Unit) {
|
||||
private fun requestBonding(
|
||||
activity: MainActivity,
|
||||
device: BluetoothDevice,
|
||||
onSuccess: () -> Unit
|
||||
) {
|
||||
SLogging.info("Starting bonding for $device")
|
||||
|
||||
// We need this receiver to get informed when the bond attempt finished
|
||||
@@ -132,7 +135,10 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||
|
||||
// If nothing was selected, by default select the first thing we see
|
||||
if (selectedMacAddr == null && entry.bonded)
|
||||
changeScanSelection(GeeksvilleApplication.currentActivity as MainActivity, addr)
|
||||
changeScanSelection(
|
||||
GeeksvilleApplication.currentActivity as MainActivity,
|
||||
addr
|
||||
)
|
||||
|
||||
devices.value = oldDevs + Pair(addr, entry) // trigger gui updates
|
||||
}
|
||||
@@ -347,14 +353,16 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||
if (!device.bonded)
|
||||
scanStatusText.setText(R.string.starting_pairing)
|
||||
|
||||
b.isSelected = scanModel.onSelected(requireActivity() as MainActivity, device)
|
||||
b.isSelected =
|
||||
scanModel.onSelected(requireActivity() as MainActivity, device)
|
||||
|
||||
if (!b.isSelected)
|
||||
scanStatusText.setText(R.string.pairing_failed)
|
||||
}
|
||||
}
|
||||
|
||||
val hasBonded = RadioInterfaceService.getBondedDeviceAddress(requireContext()) != null
|
||||
val hasBonded =
|
||||
RadioInterfaceService.getBondedDeviceAddress(requireContext()) != null
|
||||
|
||||
// get rid of the warning text once at least one device is paired
|
||||
warningNotPaired.visibility = if (hasBonded) View.GONE else View.VISIBLE
|
||||
|
||||
Reference in New Issue
Block a user