fix: clear data when changing devices (#1985)

This commit is contained in:
James Rich
2025-05-30 13:17:09 -05:00
committed by GitHub
parent ead0c43381
commit 25ecdc912e
7 changed files with 102 additions and 22 deletions

View File

@@ -31,6 +31,7 @@ import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.withContext
import javax.inject.Inject
@Suppress("TooManyFunctions")
class MeshLogRepository @Inject constructor(
private val meshLogDaoLazy: dagger.Lazy<MeshLogDao>,
private val dispatchers: CoroutineDispatchers,

View File

@@ -111,12 +111,8 @@ class NodeRepository @Inject constructor(
}
suspend fun installNodeDB(mi: MyNodeEntity, nodes: List<NodeEntity>) = withContext(dispatchers.io) {
val isDifferentNode = myNodeInfo.value?.myNodeNum != mi.myNodeNum
nodeInfoDao.clearMyNodeInfo()
nodeInfoDao.setMyNodeInfo(mi) // set MyNodeEntity first
if (isDifferentNode) {
nodeInfoDao.clearNodeInfo()
}
nodeInfoDao.putAll(nodes)
}

View File

@@ -107,4 +107,8 @@ class PacketRepository @Inject constructor(private val packetDaoLazy: dagger.Laz
suspend fun insertReaction(reaction: ReactionEntity) = withContext(Dispatchers.IO) {
packetDao.insert(reaction)
}
suspend fun clearPacketDB() = withContext(Dispatchers.IO) {
packetDao.deleteAll()
}
}

View File

@@ -214,4 +214,7 @@ interface PacketDao {
@Upsert
suspend fun insert(reaction: ReactionEntity)
@Query("DELETE FROM packet")
suspend fun deleteAll()
}

View File

@@ -81,7 +81,7 @@ class BTScanModel @Inject constructor(
}
// Include a placeholder for "None"
addDevice(DeviceListEntry(context.getString(R.string.none), "n", true))
addDevice(DeviceListEntry(context.getString(R.string.none), NO_DEVICE_SELECTED, true))
if (showMockInterface) {
addDevice(DeviceListEntry("Demo Mode", "m", true))
@@ -158,7 +158,7 @@ class BTScanModel @Inject constructor(
val selectedBluetooth: Boolean get() = selectedAddress?.getOrNull(0) == 'x'
// / Use the string for the NopInterface
val selectedNotNull: String get() = selectedAddress ?: "n"
val selectedNotNull: String get() = selectedAddress ?: NO_DEVICE_SELECTED
val scanResult = MutableLiveData<MutableMap<String, DeviceListEntry>>(mutableMapOf())
@@ -280,3 +280,5 @@ class BTScanModel @Inject constructor(
private val _spinner = MutableLiveData(false)
val spinner: LiveData<Boolean> get() = _spinner
}
const val NO_DEVICE_SELECTED = "n"

View File

@@ -21,10 +21,12 @@ import android.annotation.SuppressLint
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.ServiceInfo
import android.os.IBinder
import android.os.RemoteException
import androidx.core.app.ServiceCompat
import androidx.core.content.edit
import androidx.core.location.LocationCompat
import com.geeksville.mesh.AdminProtos
import com.geeksville.mesh.AppOnlyProtos
@@ -67,6 +69,7 @@ import com.geeksville.mesh.database.entity.Packet
import com.geeksville.mesh.database.entity.ReactionEntity
import com.geeksville.mesh.fromRadio
import com.geeksville.mesh.model.DeviceVersion
import com.geeksville.mesh.model.NO_DEVICE_SELECTED
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.model.getTracerouteResponse
import com.geeksville.mesh.position
@@ -91,6 +94,9 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -108,7 +114,9 @@ sealed class ServiceAction {
data class GetDeviceMetadata(val destNum: Int) : ServiceAction()
data class Favorite(val node: Node) : ServiceAction()
data class Ignore(val node: Node) : ServiceAction()
data class Reaction(val emoji: String, val replyId: Int, val contactKey: String) : ServiceAction()
data class Reaction(val emoji: String, val replyId: Int, val contactKey: String) :
ServiceAction()
data class AddSharedContact(val contact: AdminProtos.SharedContact) : ServiceAction()
}
@@ -231,6 +239,7 @@ class MeshService : Service(), Logging {
ConnectionState.CONNECTED -> getString(R.string.connected_count).format(
numOnlineNodes
)
ConnectionState.DISCONNECTED -> getString(R.string.disconnected)
ConnectionState.DEVICE_SLEEP -> getString(R.string.device_sleeping)
}
@@ -325,11 +334,17 @@ class MeshService : Service(), Logging {
else -> return
}
serviceNotifications.updateMessageNotification(contactKey, getSenderName(dataPacket), message)
serviceNotifications.updateMessageNotification(
contactKey,
getSenderName(dataPacket),
message
)
}
override fun onCreate() {
super.onCreate()
sharedPreferences = getSharedPreferences("mesh-prefs", Context.MODE_PRIVATE)
_lastAddress.value = sharedPreferences.getString("device_address", null) ?: NO_DEVICE_SELECTED
info("Creating mesh service")
serviceNotifications.initChannels()
@@ -367,7 +382,7 @@ class MeshService : Service(), Logging {
*/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val a = radioInterfaceService.getBondedDeviceAddress()
val wantForeground = a != null && a != "n"
val wantForeground = a != null && a != NO_DEVICE_SELECTED
info("Requesting foreground service=$wantForeground")
@@ -508,6 +523,7 @@ class MeshService : Service(), Logging {
val n = hexStr.toLong(16).toInt()
nodeDBbyNodeNum[n] ?: throw IdNotFoundException(id)
}
else -> throw InvalidNodeIdException(id)
}
}
@@ -974,6 +990,7 @@ class MeshService : Service(), Logging {
}
}
}
t.hasEnvironmentMetrics() -> it.environmentTelemetry = t
t.hasPowerMetrics() -> it.powerTelemetry = t
}
@@ -989,8 +1006,11 @@ class MeshService : Service(), Logging {
shouldDisplay = true
forceDisplay = true
}
t.deviceMetrics.batteryLevel == batteryPercentLowThreshold -> shouldDisplay = true
t.deviceMetrics.batteryLevel.mod(batteryPercentLowDivisor) == 0 && !isRemote -> shouldDisplay = true
t.deviceMetrics.batteryLevel.mod(batteryPercentLowDivisor) == 0 && !isRemote -> shouldDisplay =
true
isRemote -> shouldDisplay = true
}
if (shouldDisplay) {
@@ -1446,10 +1466,14 @@ class MeshService : Service(), Logging {
MeshProtos.FromRadio.MODULECONFIG_FIELD_NUMBER -> handleModuleConfig(proto.moduleConfig)
MeshProtos.FromRadio.QUEUESTATUS_FIELD_NUMBER -> handleQueueStatus(proto.queueStatus)
MeshProtos.FromRadio.METADATA_FIELD_NUMBER -> handleMetadata(proto.metadata)
MeshProtos.FromRadio.MQTTCLIENTPROXYMESSAGE_FIELD_NUMBER -> handleMqttProxyMessage(proto.mqttClientProxyMessage)
MeshProtos.FromRadio.MQTTCLIENTPROXYMESSAGE_FIELD_NUMBER -> handleMqttProxyMessage(
proto.mqttClientProxyMessage
)
MeshProtos.FromRadio.CLIENTNOTIFICATION_FIELD_NUMBER -> {
handleClientNotification(proto.clientNotification)
}
else -> errormsg("Unexpected FromRadio variant")
}
} catch (ex: InvalidProtocolBufferException) {
@@ -1750,7 +1774,10 @@ class MeshService : Service(), Logging {
newNodes.clear() // Just to save RAM ;-)
serviceScope.handledLaunch {
radioConfigRepository.installNodeDB(myNodeInfo!!, nodeDBbyNodeNum.values.toList())
radioConfigRepository.installNodeDB(
myNodeInfo!!,
nodeDBbyNodeNum.values.toList()
)
}
haveNodeDB = true // we now have nodes from real hardware
@@ -1807,14 +1834,20 @@ class MeshService : Service(), Logging {
handleReceivedPosition(mi.myNodeNum, position)
}
sendToRadio(newMeshPacketTo(idNum).buildMeshPacket(
channel = if (destNum == null) 0 else nodeDBbyNodeNum[destNum]?.channel ?: 0,
priority = MeshPacket.Priority.BACKGROUND,
) {
portnumValue = Portnums.PortNum.POSITION_APP_VALUE
payload = position.toByteString()
this.wantResponse = wantResponse
})
sendToRadio(
newMeshPacketTo(idNum).buildMeshPacket(
channel = if (destNum == null) {
0
} else {
nodeDBbyNodeNum[destNum]?.channel
?: 0
},
priority = MeshPacket.Priority.BACKGROUND,
) {
portnumValue = Portnums.PortNum.POSITION_APP_VALUE
payload = position.toByteString()
this.wantResponse = wantResponse
})
}
} catch (ex: BLEException) {
warn("Ignoring disconnected radio during gps location update")
@@ -1952,11 +1985,49 @@ class MeshService : Service(), Logging {
rememberReaction(packet.copy { from = myNodeNum })
}
private val _lastAddress: MutableStateFlow<String?> = MutableStateFlow(null)
val lastAddress: StateFlow<String?>
get() = _lastAddress.asStateFlow()
lateinit var sharedPreferences: SharedPreferences
fun clearDatabases() = serviceScope.handledLaunch {
debug("Clearing all databases")
radioConfigRepository.clearNodeDB()
packetRepository.get().clearPacketDB()
meshLogRepository.get().deleteAll()
}
private fun updateLastAddress(deviceAddr: String?) {
debug("setDeviceAddress: Passing through device change to radio service: ${deviceAddr.anonymize}")
when (deviceAddr) {
null, "" -> {
debug("SetDeviceAddress: No previous device address, setting new one")
_lastAddress.value = deviceAddr
sharedPreferences.edit {
putString("device_address", deviceAddr)
}
}
lastAddress.value, NO_DEVICE_SELECTED -> {
debug("SetDeviceAddress: Device address is the none or same, ignoring")
}
else -> {
debug("SetDeviceAddress: Device address changed from $lastAddress to $deviceAddr")
_lastAddress.value = deviceAddr
sharedPreferences.edit {
putString("device_address", deviceAddr)
}
clearDatabases()
}
}
}
private val binder = object : IMeshService.Stub() {
override fun setDeviceAddress(deviceAddr: String?) = toRemoteExceptions {
debug("Passing through device change to radio service: ${deviceAddr.anonymize}")
updateLastAddress(deviceAddr)
val res = radioInterfaceService.setDeviceAddress(deviceAddr)
if (res) {
discardNodeDB()
@@ -2236,12 +2307,14 @@ class MeshService : Service(), Logging {
}
override fun requestFactoryReset(requestId: Int, destNum: Int) = toRemoteExceptions {
clearDatabases()
sendToRadio(newMeshPacketTo(destNum).buildAdminPacket(id = requestId) {
factoryResetDevice = 1
})
}
override fun requestNodedbReset(requestId: Int, destNum: Int) = toRemoteExceptions {
clearDatabases()
sendToRadio(newMeshPacketTo(destNum).buildAdminPacket(id = requestId) {
nodedbReset = 1
})

View File

@@ -90,6 +90,7 @@ import com.geeksville.mesh.android.permissionMissing
import com.geeksville.mesh.database.entity.MyNodeEntity
import com.geeksville.mesh.model.BTScanModel
import com.geeksville.mesh.model.BluetoothViewModel
import com.geeksville.mesh.model.NO_DEVICE_SELECTED
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.repository.network.NetworkRepository
@@ -331,7 +332,7 @@ fun ConnectionsScreen(
.fillMaxWidth()
.selectable(
selected = (device.fullAddress == selectedDevice) ||
device.fullAddress == "n",
device.fullAddress == NO_DEVICE_SELECTED,
onClick = {
if (!device.bonded) {
uiViewModel.showSnackbar(context.getString(R.string.starting_pairing))