Skip to content

Commit

Permalink
Android: Ensuring DNS-SD service is discoverable before calling NsdMa…
Browse files Browse the repository at this point in the history
…nager.resolveService() (#99) (#25597)

* Android: Ensuring DNS-SD service is discoverable  before calling NsdManager.resolveService()

* Addressing feedback from cliffamzn@

* Stopping discovery and cancelling corresponding ScheduledFuture when service is found

* Update src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java



---------

Co-authored-by: Cliff Chung <[email protected]>
  • Loading branch information
2 people authored and pull[bot] committed Oct 11, 2023
1 parent 989277e commit 0c22785
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 56 deletions.
1 change: 1 addition & 0 deletions src/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit 0c22785

Please sign in to comment.