Skip to content

Instantly share code, notes, and snippets.

@raspberrypisig
Created August 9, 2025 04:33
Show Gist options
  • Save raspberrypisig/29bc00e856903ac5414c6bc01f79d817 to your computer and use it in GitHub Desktop.
Save raspberrypisig/29bc00e856903ac5414c6bc01f79d817 to your computer and use it in GitHub Desktop.
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.os.Build
import androidx.annotation.RequiresApi
import kotlinx.coroutines.suspendCancellableCoroutine
import java.util.concurrent.Executor
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
// ... other code ...
/**
* The core suspendable coroutine for resolving a service.
*/
private suspend fun resolveServiceInternal(
nsdManager: NsdManager,
serviceInfo: NsdServiceInfo
): DiscoveredService = suspendCancellableCoroutine { continuation ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
val executor = Executor { command -> command.run() } // You can use a more specific executor if needed
val callback = object : NsdManager.ServiceInfoCallback {
override fun onServiceInfoCallbackRegistrationFailed(errorCode: Int) {
val error = NsdException("Service info callback registration failed for ${serviceInfo.serviceName}", errorCode)
continuation.resumeWithException(error)
}
override fun onServiceUpdated(resolvedInfo: NsdServiceInfo) {
continuation.resume(resolvedInfo.toCommon())
}
override fun onServiceLost() {
// This callback indicates the service is no longer resolvable.
// You might want to handle this differently, e.g., by throwing a specific exception
// or returning null, depending on your desired behavior.
val error = NsdException("Service lost during resolution: ${serviceInfo.serviceName}", 0 /* Or a custom error code */)
continuation.resumeWithException(error)
}
override fun onServiceUnregistered() {
// This is typically for when a service *you* registered is unregistered.
// It might not be directly relevant in the context of resolving a *discovered* service,
// but you should be aware of its purpose.
}
}
nsdManager.registerServiceInfoCallback(serviceInfo, executor, callback)
continuation.invokeOnCancellation {
// Important: Unregister the callback when the coroutine is cancelled
// to avoid leaks and unnecessary work.
nsdManager.unregisterServiceInfoCallback(callback)
}
} else {
// Fallback to the deprecated version for older Android versions
@Suppress("DEPRECATION")
val listener = object : NsdManager.ResolveListener {
override fun onResolveFailed(failedServiceInfo: NsdServiceInfo, errorCode: Int) {
val error = NsdException("Failed to resolve service: ${failedServiceInfo.serviceName}", errorCode)
if (continuation.isActive) { // Check if coroutine is still active
continuation.resumeWithException(error)
}
}
override fun onServiceResolved(resolvedInfo: NsdServiceInfo) {
if (continuation.isActive) { // Check if coroutine is still active
continuation.resume(resolvedInfo.toCommon())
}
}
}
@Suppress("DEPRECATION")
nsdManager.resolveService(serviceInfo, listener)
}
}
// ... other code ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment