From 37dad9b6fa18a54b54797fefacad48d5fb5c0255 Mon Sep 17 00:00:00 2001 From: andrekir Date: Fri, 6 Oct 2023 17:54:37 -0300 Subject: [PATCH] refactor: move `IMeshService` instance to repository --- .../java/com/geeksville/mesh/MainActivity.kt | 50 ++++--------------- .../java/com/geeksville/mesh/model/UIState.kt | 2 +- .../datastore/RadioConfigRepository.kt | 5 ++ .../mesh/service/ServiceRepository.kt | 15 ++++++ 4 files changed, 30 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/com/geeksville/mesh/service/ServiceRepository.kt diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index 2f4126752..9951f5071 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -56,6 +56,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import java.text.DateFormat import java.util.Date +import javax.inject.Inject /* UI design @@ -117,6 +118,9 @@ class MainActivity : AppCompatActivity(), Logging { private val bluetoothViewModel: BluetoothViewModel by viewModels() private val model: UIViewModel by viewModels() + @Inject + internal lateinit var serviceRepository: ServiceRepository + private val bluetoothPermissionsLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result -> if (result.entries.all { it.value }) { @@ -351,7 +355,7 @@ class MainActivity : AppCompatActivity(), Logging { debug("connchange $oldConnection -> $newConnection") if (newConnection == MeshService.ConnectionState.CONNECTED) { - model.meshService?.let { service -> + serviceRepository.meshService?.let { service -> model.setConnectionState(newConnection) @@ -506,44 +510,8 @@ class MainActivity : AppCompatActivity(), Logging { IMeshService.Stub.asInterface(it) }) { override fun onConnected(service: IMeshService) { - - /* - Note: we must call this callback in a coroutine. Because apparently there is only a single activity looper thread. and if that onConnected override - also tries to do a service operation we can deadlock. - - Old buggy stack trace: - - at sun.misc.Unsafe.park (Unsafe.java) - - waiting on an unknown object - at java.util.concurrent.locks.LockSupport.park (LockSupport.java:190) - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await (AbstractQueuedSynchronizer.java:2067) - at com.geeksville.mesh.android.ServiceClient.waitConnect (ServiceClient.java:46) - at com.geeksville.mesh.android.ServiceClient.getService (ServiceClient.java:27) - at com.geeksville.mesh.service.MeshService$binder$1$setDeviceAddress$1.invoke (MeshService.java:1519) - at com.geeksville.mesh.service.MeshService$binder$1$setDeviceAddress$1.invoke (MeshService.java:1514) - at com.geeksville.mesh.util.ExceptionsKt.toRemoteExceptions (ExceptionsKt.java:56) - at com.geeksville.mesh.service.MeshService$binder$1.setDeviceAddress (MeshService.java:1516) - at com.geeksville.mesh.MainActivity$mesh$1$onConnected$1.invoke (MainActivity.java:743) - at com.geeksville.mesh.MainActivity$mesh$1$onConnected$1.invoke (MainActivity.java:734) - at com.geeksville.mesh.util.ExceptionsKt.exceptionReporter (ExceptionsKt.java:34) - at com.geeksville.mesh.MainActivity$mesh$1.onConnected (MainActivity.java:738) - at com.geeksville.mesh.MainActivity$mesh$1.onConnected (MainActivity.java:734) - at com.geeksville.mesh.android.ServiceClient$connection$1$onServiceConnected$1.invoke (ServiceClient.java:89) - at com.geeksville.mesh.android.ServiceClient$connection$1$onServiceConnected$1.invoke (ServiceClient.java:84) - at com.geeksville.mesh.util.ExceptionsKt.exceptionReporter (ExceptionsKt.java:34) - at com.geeksville.mesh.android.ServiceClient$connection$1.onServiceConnected (ServiceClient.java:85) - at android.app.LoadedApk$ServiceDispatcher.doConnected (LoadedApk.java:2067) - at android.app.LoadedApk$ServiceDispatcher$RunConnection.run (LoadedApk.java:2099) - at android.os.Handler.handleCallback (Handler.java:883) - at android.os.Handler.dispatchMessage (Handler.java:100) - at android.os.Looper.loop (Looper.java:237) - at android.app.ActivityThread.main (ActivityThread.java:8016) - at java.lang.reflect.Method.invoke (Method.java) - at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493) - at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1076) - */ connectionJob = mainScope.handledLaunch { - model.meshService = service + serviceRepository.setMeshService(service) try { usbDevice?.let { usb -> @@ -582,14 +550,14 @@ class MainActivity : AppCompatActivity(), Logging { override fun onDisconnected() { unregisterMeshReceiver() - model.meshService = null + serviceRepository.setMeshService(null) } } 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) { + if (serviceRepository.meshService != null) { /* This problem can occur if we unbind, but there is already an onConnected job waiting to run. That job runs and then makes meshService != null again I think I've fixed this by cancelling connectionJob. We'll see! */ @@ -622,7 +590,7 @@ class MainActivity : AppCompatActivity(), Logging { job.cancel("unbinding") } mesh.close() - model.meshService = null + serviceRepository.setMeshService(null) } override fun onStop() { diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index 2834a7311..afefa74d5 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -106,7 +106,7 @@ class UIViewModel @Inject constructor( ) : ViewModel(), Logging { var actionBarMenu: Menu? = null - var meshService: IMeshService? = null + val meshService: IMeshService? get() = radioConfigRepository.meshService val nodeDB = NodeDB(this) val bondedAddress get() = radioInterfaceService.getBondedDeviceAddress() diff --git a/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt b/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt index 37432052f..99dc94328 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt @@ -4,6 +4,7 @@ import com.geeksville.mesh.AppOnlyProtos.ChannelSet import com.geeksville.mesh.ChannelProtos.Channel import com.geeksville.mesh.ChannelProtos.ChannelSettings import com.geeksville.mesh.ConfigProtos.Config +import com.geeksville.mesh.IMeshService import com.geeksville.mesh.LocalOnlyProtos.LocalConfig import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig @@ -11,6 +12,7 @@ import com.geeksville.mesh.MyNodeInfo import com.geeksville.mesh.NodeInfo import com.geeksville.mesh.database.dao.MyNodeInfoDao import com.geeksville.mesh.database.dao.NodeInfoDao +import com.geeksville.mesh.service.ServiceRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.firstOrNull @@ -23,12 +25,15 @@ import javax.inject.Inject * and [ChannelSet], [LocalConfig] & [LocalModuleConfig] data stores. */ class RadioConfigRepository @Inject constructor( + private val serviceRepository: ServiceRepository, private val myNodeInfoDao: MyNodeInfoDao, private val nodeInfoDao: NodeInfoDao, private val channelSetRepository: ChannelSetRepository, private val localConfigRepository: LocalConfigRepository, private val moduleConfigRepository: ModuleConfigRepository, ) { + val meshService: IMeshService? get() = serviceRepository.meshService + suspend fun clearNodeDB() = withContext(Dispatchers.IO) { myNodeInfoDao.clearMyNodeInfo() nodeInfoDao.clearNodeInfo() diff --git a/app/src/main/java/com/geeksville/mesh/service/ServiceRepository.kt b/app/src/main/java/com/geeksville/mesh/service/ServiceRepository.kt new file mode 100644 index 000000000..3353ddd57 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/service/ServiceRepository.kt @@ -0,0 +1,15 @@ +package com.geeksville.mesh.service + +import com.geeksville.mesh.IMeshService +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ServiceRepository @Inject constructor() { + var meshService: IMeshService? = null + private set + + fun setMeshService(service: IMeshService?) { + meshService = service + } +}