diff --git a/src/platform/android/BUILD.gn b/src/platform/android/BUILD.gn index 535664a45b8b8d..3611db0267e841 100644 --- a/src/platform/android/BUILD.gn +++ b/src/platform/android/BUILD.gn @@ -91,6 +91,7 @@ android_library("java") { "java/chip/platform/NetworkInterface.java", "java/chip/platform/NsdManagerServiceBrowser.java", "java/chip/platform/NsdManagerServiceResolver.java", + "java/chip/platform/NsdServiceFinderAndResolver.java", "java/chip/platform/PreferencesConfigurationManager.java", "java/chip/platform/PreferencesKeyValueStoreManager.java", "java/chip/platform/ServiceBrowser.java", diff --git a/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java b/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java index 11b6241d2deb85..c01855172fcfe6 100644 --- a/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java +++ b/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java @@ -71,8 +71,6 @@ public void resolve( final long callbackHandle, final long contextHandle, final ChipMdnsCallback chipMdnsCallback) { - multicastLock.acquire(); - NsdServiceInfo serviceInfo = new NsdServiceInfo(); serviceInfo.setServiceName(instanceName); serviceInfo.setServiceType(serviceType); @@ -102,60 +100,18 @@ public void run() { } }; - if (nsdManagerResolverAvailState != null) { - nsdManagerResolverAvailState.acquireResolver(); - } - - this.nsdManager.resolveService( - serviceInfo, - new NsdManager.ResolveListener() { - @Override - public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { - Log.w( - TAG, - "Failed to resolve service '" + serviceInfo.getServiceName() + "': " + errorCode); - chipMdnsCallback.handleServiceResolve( - instanceName, serviceType, null, null, 0, null, callbackHandle, contextHandle); - - if (multicastLock.isHeld()) { - multicastLock.release(); - - if (nsdManagerResolverAvailState != null) { - nsdManagerResolverAvailState.signalFree(); - } - } - mainThreadHandler.removeCallbacks(timeoutRunnable); - } - - @Override - public void onServiceResolved(NsdServiceInfo serviceInfo) { - Log.i( - TAG, - "Resolved service '" - + serviceInfo.getServiceName() - + "' to " - + serviceInfo.getHost()); - // TODO: Find out if DNS-SD results for Android should contain interface ID - chipMdnsCallback.handleServiceResolve( - instanceName, - serviceType, - serviceInfo.getHost().getHostName(), - serviceInfo.getHost().getHostAddress(), - serviceInfo.getPort(), - serviceInfo.getAttributes(), - callbackHandle, - contextHandle); - - if (multicastLock.isHeld()) { - multicastLock.release(); - - if (nsdManagerResolverAvailState != null) { - nsdManagerResolverAvailState.signalFree(); - } - } - mainThreadHandler.removeCallbacks(timeoutRunnable); - } - }); + NsdServiceFinderAndResolver serviceFinderResolver = + new NsdServiceFinderAndResolver( + this.nsdManager, + serviceInfo, + callbackHandle, + contextHandle, + chipMdnsCallback, + timeoutRunnable, + multicastLock, + mainThreadHandler, + nsdManagerResolverAvailState); + serviceFinderResolver.start(); mainThreadHandler.postDelayed(timeoutRunnable, RESOLVE_SERVICE_TIMEOUT); } diff --git a/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java b/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java new file mode 100644 index 00000000000000..743c1b97e19602 --- /dev/null +++ b/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package chip.platform; + +import android.net.nsd.NsdManager; +import android.net.nsd.NsdServiceInfo; +import android.net.wifi.WifiManager.MulticastLock; +import android.os.Handler; +import android.util.Log; +import androidx.annotation.Nullable; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +class NsdServiceFinderAndResolver implements NsdManager.DiscoveryListener { + private static final String TAG = NsdServiceFinderAndResolver.class.getSimpleName(); + + private static final long BROWSE_SERVICE_TIMEOUT_MS = 5000L; + + private final NsdManager nsdManager; + private final NsdServiceInfo targetServiceInfo; + private final long callbackHandle; + private final long contextHandle; + private final ChipMdnsCallback chipMdnsCallback; + private final Runnable timeoutRunnable; + private final MulticastLock multicastLock; + private final Handler mainThreadHandler; + + @Nullable + private final NsdManagerServiceResolver.NsdManagerResolverAvailState nsdManagerResolverAvailState; + + private ScheduledFuture stopDiscoveryRunnable; + + public NsdServiceFinderAndResolver( + final NsdManager nsdManager, + final NsdServiceInfo targetServiceInfo, + final long callbackHandle, + final long contextHandle, + final ChipMdnsCallback chipMdnsCallback, + final Runnable timeoutRunnable, + final MulticastLock multicastLock, + final Handler mainThreadHandler, + final NsdManagerServiceResolver.NsdManagerResolverAvailState nsdManagerResolverAvailState) { + this.nsdManager = nsdManager; + this.targetServiceInfo = targetServiceInfo; + this.callbackHandle = callbackHandle; + this.contextHandle = contextHandle; + this.chipMdnsCallback = chipMdnsCallback; + this.timeoutRunnable = timeoutRunnable; + this.multicastLock = multicastLock; + this.mainThreadHandler = mainThreadHandler; + this.nsdManagerResolverAvailState = nsdManagerResolverAvailState; + } + + public void start() { + multicastLock.acquire(); + + this.nsdManager.discoverServices( + targetServiceInfo.getServiceType(), NsdManager.PROTOCOL_DNS_SD, this); + + NsdServiceFinderAndResolver serviceFinderResolver = this; + this.stopDiscoveryRunnable = + Executors.newSingleThreadScheduledExecutor() + .schedule( + new Runnable() { + @Override + public void run() { + Log.d( + TAG, + "Service discovery timed out after " + BROWSE_SERVICE_TIMEOUT_MS + " ms"); + nsdManager.stopServiceDiscovery(serviceFinderResolver); + if (multicastLock.isHeld()) { + multicastLock.release(); + } + } + }, + BROWSE_SERVICE_TIMEOUT_MS, + TimeUnit.MILLISECONDS); + } + + @Override + public void onServiceFound(NsdServiceInfo service) { + if (targetServiceInfo.getServiceName().equals(service.getServiceName())) { + Log.d(TAG, "onServiceFound: found target service " + service); + + if (stopDiscoveryRunnable.cancel(false)) { + nsdManager.stopServiceDiscovery(this); + if (multicastLock.isHeld()) { + multicastLock.release(); + } + } + + if (nsdManagerResolverAvailState != null) { + nsdManagerResolverAvailState.acquireResolver(); + } + + resolveService(service, callbackHandle, contextHandle, chipMdnsCallback, timeoutRunnable); + } else { + Log.d(TAG, "onServiceFound: found service not a target for resolution, ignoring " + service); + } + } + + private void resolveService( + NsdServiceInfo serviceInfo, + final long callbackHandle, + final long contextHandle, + final ChipMdnsCallback chipMdnsCallback, + Runnable timeoutRunnable) { + this.nsdManager.resolveService( + serviceInfo, + new NsdManager.ResolveListener() { + @Override + public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { + Log.w( + TAG, + "Failed to resolve service '" + serviceInfo.getServiceName() + "': " + errorCode); + chipMdnsCallback.handleServiceResolve( + serviceInfo.getServiceName(), + serviceInfo.getServiceType(), + null, + null, + 0, + null, + callbackHandle, + contextHandle); + + if (multicastLock.isHeld()) { + multicastLock.release(); + + if (nsdManagerResolverAvailState != null) { + nsdManagerResolverAvailState.signalFree(); + } + } + mainThreadHandler.removeCallbacks(timeoutRunnable); + } + + @Override + public void onServiceResolved(NsdServiceInfo serviceInfo) { + Log.i( + TAG, + "Resolved service '" + + serviceInfo.getServiceName() + + "' to " + + serviceInfo.getHost()); + // TODO: Find out if DNS-SD results for Android should contain interface ID + chipMdnsCallback.handleServiceResolve( + serviceInfo.getServiceName(), + serviceInfo.getServiceType(), + serviceInfo.getHost().getHostName(), + serviceInfo.getHost().getHostAddress(), + serviceInfo.getPort(), + serviceInfo.getAttributes(), + callbackHandle, + contextHandle); + + if (multicastLock.isHeld()) { + multicastLock.release(); + + if (nsdManagerResolverAvailState != null) { + nsdManagerResolverAvailState.signalFree(); + } + } + mainThreadHandler.removeCallbacks(timeoutRunnable); + } + }); + } + + @Override + public void onDiscoveryStarted(String regType) { + Log.d(TAG, "Service discovery started. regType: " + regType); + } + + @Override + public void onServiceLost(NsdServiceInfo service) { + Log.e(TAG, "Service lost: " + service); + } + + @Override + public void onDiscoveryStopped(String serviceType) { + Log.i(TAG, "Discovery stopped: " + serviceType); + } + + @Override + public void onStartDiscoveryFailed(String serviceType, int errorCode) { + Log.e(TAG, "Discovery failed to start: Error code: " + errorCode); + } + + @Override + public void onStopDiscoveryFailed(String serviceType, int errorCode) { + Log.e(TAG, "Discovery failed to stop: Error code: " + errorCode); + } +}