Skip to content

Commit

Permalink
refactor: move IMeshService instance to repository
Browse files Browse the repository at this point in the history
  • Loading branch information
andrekir committed Oct 6, 2023
1 parent e93fac2 commit 37dad9b
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 42 deletions.
50 changes: 9 additions & 41 deletions app/src/main/java/com/geeksville/mesh/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 }) {
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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 ->
Expand Down Expand Up @@ -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!
*/
Expand Down Expand Up @@ -622,7 +590,7 @@ class MainActivity : AppCompatActivity(), Logging {
job.cancel("unbinding")
}
mesh.close()
model.meshService = null
serviceRepository.setMeshService(null)
}

override fun onStop() {
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/geeksville/mesh/model/UIState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ 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
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
Expand All @@ -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()
Expand Down
15 changes: 15 additions & 0 deletions app/src/main/java/com/geeksville/mesh/service/ServiceRepository.kt
Original file line number Diff line number Diff line change
@@ -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
}
}

0 comments on commit 37dad9b

Please sign in to comment.