Skip to content

Commit

Permalink
Re-implement Discovery
Browse files Browse the repository at this point in the history
- Convert Discovery class to object
- Simplify cross-thread handling with dedicated executor
- Fixed simultaneous resolution of multiple services
- Acquire multicast lock to improve reliability
  • Loading branch information
gujjwal00 committed Feb 22, 2024
1 parent d5f2639 commit 021145a
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 115 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2024 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/

package com.gaurav.avnc.viewmodel.service

import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.util.Log
import androidx.core.content.ContextCompat
import com.gaurav.avnc.pollingAssert
import com.gaurav.avnc.runOnMainSync
import com.gaurav.avnc.targetContext
import org.junit.After
import org.junit.Assert.assertTrue
import org.junit.Assume
import org.junit.Before
import org.junit.Test

class DiscoveryTest {
private val TAG = "DiscoveryTest"
private var nsdManager: NsdManager? = null
private var listeners = mutableListOf<NsdManager.RegistrationListener>()

/**
* If advertisement fails, this method will cause the calling test to be skipped.
* Advertisement can fail if there is no suitable network on the device
* (e.g. in Airplane mode on newer Android versions).
*/
private fun advertiseService(advertisedName: String, advertisedPort: Int) {
var registeredService = false
val listener = object : NsdManager.RegistrationListener {
override fun onRegistrationFailed(serviceInfo: NsdServiceInfo?, errorCode: Int) {
Log.e(TAG, "Registration failed: si: $serviceInfo, error:$errorCode")
}

override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo?, errorCode: Int) {}
override fun onServiceRegistered(serviceInfo: NsdServiceInfo?) {
Log.d(TAG, "Registered si: $serviceInfo")
registeredService = true
}

override fun onServiceUnregistered(serviceInfo: NsdServiceInfo?) {}
}
val si = NsdServiceInfo().apply {
serviceType = "_rfb._tcp"
serviceName = advertisedName
port = advertisedPort
}
nsdManager?.registerService(si, NsdManager.PROTOCOL_DNS_SD, listener)
listeners.add(listener)

Assume.assumeTrue(runCatching { pollingAssert { assertTrue(registeredService) } }.isSuccess)
}

private fun assertDiscoveryState(test: Discovery.() -> Boolean) {
pollingAssert { runOnMainSync { assertTrue(Discovery.test()) } }
}

@Before
fun before() {
nsdManager = ContextCompat.getSystemService(targetContext, NsdManager::class.java)
}

@After
fun after() {
assertDiscoveryState { Log.d(TAG, "AfterTest: state: ${isRunning.value}, list: ${servers.value}"); true }
listeners.forEach { nsdManager?.unregisterService(it) }
listeners.clear()
assertDiscoveryState { servers.value!!.isEmpty() }
Discovery.stop()
assertDiscoveryState { isRunning.value == false }
}

@Test
fun startStop() {
assertDiscoveryState { isRunning.value == false }
Discovery.start(targetContext)
assertDiscoveryState { isRunning.value == true }
Discovery.stop()
assertDiscoveryState { isRunning.value == false }
}

@Test
fun singleService() {
Discovery.start(targetContext)
advertiseService("Server 1", 5999)
assertDiscoveryState {
isRunning.value == true && servers.value!!.size == 1 && servers.value!![0].port == 5999
}
}

@Test
fun multipleServices() {
Discovery.start(targetContext)

val count = 10
for (i in 1..count)
advertiseService("Server $i", 5900 + i)

assertDiscoveryState {
isRunning.value == true && servers.value!!.size == count &&
servers.value!!.find { it.port == 5901 } != null &&
servers.value!!.find { it.port == 5904 } != null &&
servers.value!!.find { it.port == 5908 } != null
}
}
}
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />

<application
android:name=".App"
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/java/com/gaurav/avnc/viewmodel/HomeViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import android.app.Application
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.switchMap
import androidx.lifecycle.viewModelScope
import com.gaurav.avnc.model.ServerProfile
import com.gaurav.avnc.util.LiveEvent
import com.gaurav.avnc.viewmodel.service.Discovery
Expand All @@ -33,7 +32,7 @@ class HomeViewModel(app: Application) : BaseViewModel(app) {
/**
* Used to find new servers.
*/
val discovery by lazy { Discovery(app) }
val discovery = Discovery

/**
* Used for starting new VNC connections.
Expand Down Expand Up @@ -72,7 +71,7 @@ class HomeViewModel(app: Application) : BaseViewModel(app) {

fun startDiscovery() {
autoStopped = false
discovery.start(viewModelScope)
discovery.start(app)
}

fun stopDiscovery() {
Expand Down
Loading

0 comments on commit 021145a

Please sign in to comment.