Merge pull request #430 from meshtastic/nsd

add network service discovery
pull/431/head
Andre Kirchhoff 2022-05-17 00:54:21 -03:00 zatwierdzone przez GitHub
commit 7e8b7bc833
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
4 zmienionych plików z 136 dodań i 6 usunięć

Wyświetl plik

@ -0,0 +1,97 @@
package com.geeksville.mesh.repository.nsd
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import com.geeksville.android.Logging
import com.geeksville.mesh.CoroutineDispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.collections.ArrayList
@Singleton
class NsdRepository @Inject constructor(
private val dispatchers: CoroutineDispatchers,
private val nsdManagerLazy: dagger.Lazy<NsdManager?>,
) : Logging {
private val resolveQueue = Semaphore(1)
private var hostsList: ArrayList<NsdServiceInfo>? = ArrayList()
val resolvedList: List<NsdServiceInfo>? get() = hostsList
private val _networkDiscovery: Flow<NsdServiceInfo> = callbackFlow {
val discoveryListener = object : NsdManager.DiscoveryListener {
override fun onDiscoveryStarted(regType: String) {
debug("Service discovery started: $regType")
hostsList?.clear()
}
override fun onServiceFound(service: NsdServiceInfo) {
debug("Service discovery success: $service")
if (service.serviceType == SERVICE_TYPE &&
service.serviceName.contains(serviceName)
) {
val resolveListener = object : NsdManager.ResolveListener {
override fun onServiceResolved(service: NsdServiceInfo) {
debug("Resolve Succeeded: $service")
hostsList?.add(service)
trySend(service)
}
override fun onResolveFailed(service: NsdServiceInfo, errorCode: Int) {
debug("Resolve failed: $service - Error code: $errorCode")
}
}
// one resolveService at a time to avoid: Error Code 3: Failure Already active
launch {
try {
resolveQueue.acquire()
nsdManagerLazy.get()?.resolveService(service, resolveListener)
} finally {
resolveQueue.release()
}
}
} else {
debug("Not our Service - Name: ${service.serviceName}, Type: ${service.serviceType}")
}
}
override fun onServiceLost(service: NsdServiceInfo) {
debug("Service lost: $service")
}
override fun onDiscoveryStopped(serviceType: String) {
debug("Discovery stopped: $serviceType")
}
override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
debug("Start Discovery failed: Error code: $errorCode")
nsdManagerLazy.get()?.stopServiceDiscovery(this)
}
override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
debug("Stop Discovery failed: Error code: $errorCode")
nsdManagerLazy.get()?.stopServiceDiscovery(this)
}
}
nsdManagerLazy.get()
?.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)
awaitClose { nsdManagerLazy.get()?.stopServiceDiscovery(discoveryListener) }
}.flowOn(dispatchers.default)
fun networkDiscoveryFlow(): Flow<NsdServiceInfo> {
return _networkDiscovery
}
companion object {
//To find all the available networks SERVICE_TYPE = "_services._dns-sd._udp"
const val SERVICE_TYPE = "_http._tcp."
const val serviceName = "Meshtastic"
}
}

Wyświetl plik

@ -0,0 +1,20 @@
package com.geeksville.mesh.repository.nsd
import android.app.Application
import android.content.Context
import android.net.nsd.NsdManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
class NsdRepositoryModule {
companion object {
@Provides
fun provideNsdManager(application: Application): NsdManager? {
return application.getSystemService(Context.NSD_SERVICE) as NsdManager?
}
}
}

Wyświetl plik

@ -87,7 +87,6 @@ class TCPInterface(service: RadioInterfaceService, private val address: String)
} else
readChar(c.toByte())
} catch (ex: SocketTimeoutException) {
errormsg("SocketTimeoutException in TCP reader: $ex")
// Ignore and start another read
}
}

Wyświetl plik

@ -42,6 +42,7 @@ import com.geeksville.mesh.databinding.SettingsFragmentBinding
import com.geeksville.mesh.model.BluetoothViewModel
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.repository.bluetooth.BluetoothRepository
import com.geeksville.mesh.repository.nsd.NsdRepository
import com.geeksville.mesh.repository.radio.MockInterface
import com.geeksville.mesh.repository.radio.RadioInterfaceService
import com.geeksville.mesh.repository.radio.SerialInterface
@ -59,7 +60,11 @@ import com.google.android.material.snackbar.Snackbar
import com.hoho.android.usbserial.driver.UsbSerialDriver
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import java.util.regex.Pattern
import javax.inject.Inject
@ -121,7 +126,7 @@ class BTScanModel @Inject constructor(
private val application: Application,
private val bluetoothRepository: BluetoothRepository,
private val usbRepository: UsbRepository,
// private val nsdRepository: NsdRepository,
private val nsdRepository: NsdRepository,
) : ViewModel(), Logging {
private val context: Context get() = application.applicationContext
@ -233,6 +238,9 @@ class BTScanModel @Inject constructor(
@SuppressLint("MissingPermission")
fun stopScan() {
// Stop Network Service Discovery (for TCP)
networkDiscovery?.cancel()
if (scanner != null) {
debug("stopping scan")
try {
@ -288,9 +296,9 @@ class BTScanModel @Inject constructor(
addDeviceAssociations()
// Include Network Service Discovery
// nsdRepository.resolvedList?.forEach { service ->
// addDevice(TCPDeviceListEntry(service))
// }
nsdRepository.resolvedList?.forEach { service ->
addDevice(TCPDeviceListEntry(service))
}
val serialDevices by lazy { usbRepository.serialDevicesWithDrivers.value }
serialDevices.forEach { (_, d) ->
@ -303,7 +311,13 @@ class BTScanModel @Inject constructor(
}
}
fun startScan () {
private var networkDiscovery: Job? = null
fun startScan() {
// Start Network Service Discovery (find TCP devices)
networkDiscovery = nsdRepository.networkDiscoveryFlow()
.onEach { addDevice(TCPDeviceListEntry(it)) }
.launchIn(CoroutineScope(Dispatchers.Main))
if (hasCompanionDeviceApi) {
startCompanionScan()
} else startClassicScan()