From cb8d1871c90c31cd97e096f4a12c5995e4c6a413 Mon Sep 17 00:00:00 2001
From: Mac DeCourcy <49794076+mdecourcy@users.noreply.github.com>
Date: Sun, 9 Nov 2025 08:54:21 -0800
Subject: [PATCH] feat: per device DB manager (#3641)
---
.../geeksville/mesh/MeshUtilApplication.kt | 22 ++
.../geeksville/mesh/service/MeshService.kt | 13 +-
core/data/build.gradle.kts | 3 +
.../data/datasource/NodeInfoReadDataSource.kt | 41 ++++
.../datasource/NodeInfoWriteDataSource.kt | 40 ++++
.../SwitchingNodeInfoReadDataSource.kt | 60 +++++
.../SwitchingNodeInfoWriteDataSource.kt | 60 +++++
.../core/data/di/NodeDataSourceModule.kt | 38 +++
.../core/data/repository/MeshLogRepository.kt | 38 +--
.../core/data/repository/NodeRepository.kt | 55 +++--
.../core/data/repository/PacketRepository.kt | 87 ++++---
.../repository/QuickChatActionRepository.kt | 23 +-
.../core/database/DatabaseManager.kt | 218 ++++++++++++++++++
core/strings/src/main/res/values/strings.xml | 2 +
.../feature/settings/SettingsScreen.kt | 18 ++
.../feature/settings/SettingsViewModel.kt | 11 +
16 files changed, 643 insertions(+), 86 deletions(-)
create mode 100644 core/data/src/main/kotlin/org/meshtastic/core/data/datasource/NodeInfoReadDataSource.kt
create mode 100644 core/data/src/main/kotlin/org/meshtastic/core/data/datasource/NodeInfoWriteDataSource.kt
create mode 100644 core/data/src/main/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoReadDataSource.kt
create mode 100644 core/data/src/main/kotlin/org/meshtastic/core/data/datasource/SwitchingNodeInfoWriteDataSource.kt
create mode 100644 core/data/src/main/kotlin/org/meshtastic/core/data/di/NodeDataSourceModule.kt
create mode 100644 core/database/src/main/kotlin/org/meshtastic/core/database/DatabaseManager.kt
diff --git a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt b/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
index e6a744cdd..11feaa972 100644
--- a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
+++ b/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
@@ -18,7 +18,16 @@
package com.geeksville.mesh
import android.app.Application
+import dagger.hilt.EntryPoint
+import dagger.hilt.InstallIn
+import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.android.HiltAndroidApp
+import dagger.hilt.components.SingletonComponent
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import org.meshtastic.core.database.DatabaseManager
+import org.meshtastic.core.prefs.mesh.MeshPrefs
import timber.log.Timber
/**
@@ -33,9 +42,22 @@ class MeshUtilApplication : Application() {
override fun onCreate() {
super.onCreate()
initializeMaps(this)
+ // Initialize DatabaseManager asynchronously with current device address so DAO consumers have an active DB
+ val entryPoint = EntryPointAccessors.fromApplication(this, AppEntryPoint::class.java)
+ CoroutineScope(Dispatchers.Default).launch {
+ entryPoint.databaseManager().init(entryPoint.meshPrefs().deviceAddress)
+ }
}
}
+@EntryPoint
+@InstallIn(SingletonComponent::class)
+interface AppEntryPoint {
+ fun databaseManager(): DatabaseManager
+
+ fun meshPrefs(): MeshPrefs
+}
+
fun logAssert(executeReliableWrite: Boolean) {
if (!executeReliableWrite) {
val ex = AssertionError("Assertion failed")
diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt
index d7e3694dc..080903543 100644
--- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt
+++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt
@@ -58,6 +58,7 @@ import org.meshtastic.core.data.repository.MeshLogRepository
import org.meshtastic.core.data.repository.NodeRepository
import org.meshtastic.core.data.repository.PacketRepository
import org.meshtastic.core.data.repository.RadioConfigRepository
+import org.meshtastic.core.database.DatabaseManager
import org.meshtastic.core.database.entity.MeshLog
import org.meshtastic.core.database.entity.MetadataEntity
import org.meshtastic.core.database.entity.MyNodeEntity
@@ -138,6 +139,8 @@ class MeshService : Service() {
@Inject lateinit var nodeRepository: NodeRepository
+ @Inject lateinit var databaseManager: DatabaseManager
+
@Inject lateinit var mqttRepository: MQTTRepository
@Inject lateinit var serviceNotifications: MeshServiceNotifications
@@ -2008,8 +2011,14 @@ class MeshService : Service() {
"SetDeviceAddress: Device address changed from ${currentAddr.anonymize} to ${deviceAddr.anonymize}",
)
meshPrefs.deviceAddress = deviceAddr
- clearDatabases()
- clearNotifications()
+ serviceScope.handledLaunch {
+ // Clear only in-memory caches to avoid cross-device bleed
+ discardNodeDB()
+ // Switch active on-disk DB to device-specific database
+ databaseManager.switchActiveDatabase(deviceAddr)
+ // Do not clear packet DB here; messages are per-device and should persist
+ clearNotifications()
+ }
} else {
Timber.d("SetDeviceAddress: Device address is unchanged, ignoring.")
}
diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts
index 20af2f624..1c0b72496 100644
--- a/core/data/build.gradle.kts
+++ b/core/data/build.gradle.kts
@@ -34,6 +34,9 @@ dependencies {
implementation(projects.core.prefs)
implementation(projects.core.proto)
+ // Needed because core:data references MeshtasticDatabase (supertype RoomDatabase)
+ implementation(libs.androidx.room.runtime)
+
implementation(libs.androidx.core.location.altitude)
implementation(libs.kotlinx.serialization.json)
implementation(libs.timber)
diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/NodeInfoReadDataSource.kt b/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/NodeInfoReadDataSource.kt
new file mode 100644
index 000000000..1e77cf25a
--- /dev/null
+++ b/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/NodeInfoReadDataSource.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2025 Meshtastic LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.meshtastic.core.data.datasource
+
+import kotlinx.coroutines.flow.Flow
+import org.meshtastic.core.database.entity.MyNodeEntity
+import org.meshtastic.core.database.entity.NodeEntity
+import org.meshtastic.core.database.entity.NodeWithRelations
+
+interface NodeInfoReadDataSource {
+ fun myNodeInfoFlow(): Flow
+
+ fun nodeDBbyNumFlow(): Flow