diff --git a/examples/tv-casting-app/android/App/app/build.gradle b/examples/tv-casting-app/android/App/app/build.gradle
index 99aabfdba51594..03d8fed015e6ca 100644
--- a/examples/tv-casting-app/android/App/app/build.gradle
+++ b/examples/tv-casting-app/android/App/app/build.gradle
@@ -37,6 +37,10 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}
+ testOptions {
+ unitTests.returnDefaultValues = true
+ }
+
sourceSets {
main {
jniLibs.srcDirs = ['libs/jniLibs']
@@ -59,6 +63,7 @@ dependencies {
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.+'
+ testImplementation 'org.mockito:mockito-core:3.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'com.google.zxing:core:3.3.0'
diff --git a/examples/tv-casting-app/android/App/app/src/main/AndroidManifest.xml b/examples/tv-casting-app/android/App/app/src/main/AndroidManifest.xml
index e379d2a7675095..55c3e48357d58b 100644
--- a/examples/tv-casting-app/android/App/app/src/main/AndroidManifest.xml
+++ b/examples/tv-casting-app/android/App/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
+ package="com.chip.casting.app">
@@ -31,4 +31,4 @@
-
\ No newline at end of file
+
diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/MainActivity.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/MainActivity.java
deleted file mode 100644
index 74928ce65079a6..00000000000000
--- a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/MainActivity.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.chip.casting;
-
-import android.os.Bundle;
-import android.widget.TextView;
-import androidx.appcompat.app.AppCompatActivity;
-
-public class MainActivity extends AppCompatActivity {
-
- private TextView mHelloCastingTxt;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mHelloCastingTxt = findViewById(R.id.helloCastingTxt);
- mHelloCastingTxt.setText("Hello, TV Casting app!");
- }
-}
diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/CastingContext.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/CastingContext.java
new file mode 100644
index 00000000000000..d13e04ce40ad30
--- /dev/null
+++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/CastingContext.java
@@ -0,0 +1,27 @@
+package com.chip.casting.app;
+
+import android.content.Context;
+import android.net.nsd.NsdManager;
+import android.widget.LinearLayout;
+import androidx.fragment.app.FragmentActivity;
+
+public class CastingContext {
+ private FragmentActivity fragmentActivity;
+
+ public CastingContext(FragmentActivity fragmentActivity) {
+ this.fragmentActivity = fragmentActivity;
+ }
+
+ public Context getApplicationContext() {
+ return fragmentActivity.getApplicationContext();
+ }
+
+ public NsdManager getNsdManager() {
+ return (NsdManager)
+ fragmentActivity.getApplicationContext().getSystemService(Context.NSD_SERVICE);
+ }
+
+ public LinearLayout getCommissionersLayout() {
+ return (LinearLayout) fragmentActivity.findViewById(R.id.castingCommissioners);
+ }
+}
diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/ChipTvCastingApplication.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/ChipTvCastingApplication.java
similarity index 84%
rename from examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/ChipTvCastingApplication.java
rename to examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/ChipTvCastingApplication.java
index 4a3873d84618f1..4986cc28e49c01 100644
--- a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/ChipTvCastingApplication.java
+++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/ChipTvCastingApplication.java
@@ -1,4 +1,4 @@
-package com.chip.casting;
+package com.chip.casting.app;
import android.app.Application;
diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/MainActivity.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/MainActivity.java
new file mode 100644
index 00000000000000..bbfabb68b93ed4
--- /dev/null
+++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/MainActivity.java
@@ -0,0 +1,51 @@
+package com.chip.casting.app;
+
+import android.content.Context;
+import android.net.nsd.NsdManager;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import androidx.appcompat.app.AppCompatActivity;
+import com.chip.casting.dnssd.CommissionerDiscoveryListener;
+import com.chip.casting.util.GlobalCastingConstants;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+public class MainActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ startCommissionerDiscovery();
+ }
+
+ private void startCommissionerDiscovery() {
+ WifiManager wifi = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ WifiManager.MulticastLock multicastLock = wifi.createMulticastLock("multicastLock");
+ multicastLock.setReferenceCounted(true);
+ multicastLock.acquire();
+
+ CastingContext castingContext = new CastingContext(this);
+ NsdManager.DiscoveryListener commissionerDiscoveryListener =
+ new CommissionerDiscoveryListener(castingContext);
+
+ NsdManager nsdManager = castingContext.getNsdManager();
+ nsdManager.discoverServices(
+ GlobalCastingConstants.CommissionerServiceType,
+ NsdManager.PROTOCOL_DNS_SD,
+ commissionerDiscoveryListener);
+
+ // Stop discovery after specified timeout
+ Executors.newSingleThreadScheduledExecutor()
+ .schedule(
+ new Runnable() {
+ @Override
+ public void run() {
+ nsdManager.stopServiceDiscovery(commissionerDiscoveryListener);
+ multicastLock.release();
+ }
+ },
+ 10,
+ TimeUnit.SECONDS);
+ }
+}
diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/dnssd/CommissionerDiscoveryListener.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/dnssd/CommissionerDiscoveryListener.java
new file mode 100644
index 00000000000000..3c563cbfc1c6a9
--- /dev/null
+++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/dnssd/CommissionerDiscoveryListener.java
@@ -0,0 +1,62 @@
+package com.chip.casting.dnssd;
+
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import android.util.Log;
+import com.chip.casting.app.CastingContext;
+import com.chip.casting.util.GlobalCastingConstants;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CommissionerDiscoveryListener implements NsdManager.DiscoveryListener {
+
+ private static final String TAG = CommissionerDiscoveryListener.class.getSimpleName();
+
+ private final CastingContext castingContext;
+ private final List commissioners = new ArrayList<>();
+
+ public CommissionerDiscoveryListener(CastingContext castingContext) {
+ this.castingContext = castingContext;
+ }
+
+ @Override
+ public void onDiscoveryStarted(String regType) {
+ Log.d(TAG, "Service discovery started. regType: " + regType);
+ }
+
+ @Override
+ public void onServiceFound(NsdServiceInfo service) {
+ Log.d(TAG, "Service discovery success. " + service);
+ if (service.getServiceType().equals(GlobalCastingConstants.CommissionerServiceType)) {
+ castingContext
+ .getNsdManager()
+ .resolveService(service, new CommissionerResolveListener(castingContext, commissioners));
+ } else {
+ Log.d(TAG, "Ignoring discovered service: " + service.toString());
+ }
+ }
+
+ @Override
+ public void onServiceLost(NsdServiceInfo service) {
+ // When the network service is no longer available.
+ // Internal bookkeeping code goes here.
+ 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);
+ castingContext.getNsdManager().stopServiceDiscovery(this);
+ }
+
+ @Override
+ public void onStopDiscoveryFailed(String serviceType, int errorCode) {
+ Log.e(TAG, "Discovery failed to stop: Error code:" + errorCode);
+ castingContext.getNsdManager().stopServiceDiscovery(this);
+ }
+}
diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/dnssd/CommissionerResolveListener.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/dnssd/CommissionerResolveListener.java
new file mode 100644
index 00000000000000..ea8fe3dc402e71
--- /dev/null
+++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/dnssd/CommissionerResolveListener.java
@@ -0,0 +1,75 @@
+package com.chip.casting.dnssd;
+
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.widget.Button;
+import androidx.annotation.VisibleForTesting;
+import com.chip.casting.app.CastingContext;
+import java.util.List;
+
+public class CommissionerResolveListener implements NsdManager.ResolveListener {
+
+ private static final String TAG = CommissionerResolveListener.class.getSimpleName();
+ private final CastingContext castingContext;
+ private final List commissioners;
+
+ public CommissionerResolveListener(
+ CastingContext castingContext, List commissioners) {
+ this.castingContext = castingContext;
+ this.commissioners = commissioners;
+ }
+
+ @Override
+ public void onServiceResolved(NsdServiceInfo serviceInfo) {
+ DiscoveredNodeData commissioner = new DiscoveredNodeData(serviceInfo);
+ commissioners.add(commissioner);
+ Log.d(TAG, "Commissioner resolved: " + commissioner);
+
+ String buttonText = getCommissionerButtonText(commissioner);
+ if (!buttonText.isEmpty()) {
+ Button commissionerButton = new Button(castingContext.getApplicationContext());
+ commissionerButton.setText(buttonText);
+ new Handler(Looper.getMainLooper())
+ .post(() -> castingContext.getCommissionersLayout().addView(commissionerButton));
+ } else Log.e(TAG, "Skipped displaying " + commissioner);
+ }
+
+ @Override
+ public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
+ switch (errorCode) {
+ case NsdManager.FAILURE_ALREADY_ACTIVE:
+ Log.e(TAG, "FAILURE_ALREADY_ACTIVE - Service: " + serviceInfo);
+ castingContext
+ .getNsdManager()
+ .resolveService(
+ serviceInfo, new CommissionerResolveListener(castingContext, commissioners));
+ break;
+ case NsdManager.FAILURE_INTERNAL_ERROR:
+ Log.e(TAG, "FAILURE_INTERNAL_ERROR - Service: " + serviceInfo);
+ break;
+ case NsdManager.FAILURE_MAX_LIMIT:
+ Log.e(TAG, "FAILURE_MAX_LIMIT - Service: " + serviceInfo);
+ break;
+ }
+ }
+
+ @VisibleForTesting
+ public String getCommissionerButtonText(DiscoveredNodeData commissioner) {
+ String main = commissioner.getDeviceName() != null ? commissioner.getDeviceName() : "";
+ String aux =
+ "" + (commissioner.getProductId() > 0 ? "Product ID: " + commissioner.getProductId() : "");
+ aux +=
+ commissioner.getDeviceType() > 0
+ ? (aux.isEmpty() ? "" : " ") + "Device Type: " + commissioner.getDeviceType()
+ : "";
+ aux +=
+ commissioner.getVendorId() > 0
+ ? (aux.isEmpty() ? "" : " from ") + "Vendor ID: " + commissioner.getVendorId()
+ : "";
+ aux = aux.isEmpty() ? aux : "\n[" + aux + "]";
+ return main + aux;
+ }
+}
diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/dnssd/DiscoveredNodeData.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/dnssd/DiscoveredNodeData.java
new file mode 100644
index 00000000000000..0022a42c1252af
--- /dev/null
+++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/dnssd/DiscoveredNodeData.java
@@ -0,0 +1,210 @@
+package com.chip.casting.dnssd;
+
+import android.net.nsd.NsdServiceInfo;
+import java.net.InetAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class DiscoveredNodeData {
+ private static final int MAX_IP_ADDRESSES = 5;
+ private static final int MAX_ROTATING_ID_LEN = 50;
+ private static final String KEY_DEVICE_NAME = "DN";
+ private static final String KEY_DEVICE_TYPE = "DT";
+ private static final String KEY_VENDOR_PRODUCT = "VP";
+
+ private String hostName;
+ private String instanceName;
+ private long longDiscriminator;
+ private long vendorId;
+ private long productId;
+ private byte commissioningMode;
+ private long deviceType;
+ private String deviceName;
+ private byte rotatingId[] = new byte[MAX_ROTATING_ID_LEN];
+ private int rotatingIdLen;
+ private short pairingHint;
+ private String pairingInstruction;
+ private short port;
+ private int numIPs;
+ private List ipAddresses;
+
+ public DiscoveredNodeData(NsdServiceInfo serviceInfo) {
+ Map attributes = serviceInfo.getAttributes();
+ this.deviceName = new String(attributes.get(KEY_DEVICE_NAME), StandardCharsets.UTF_8);
+ this.deviceType =
+ Long.parseLong(new String(attributes.get(KEY_DEVICE_TYPE), StandardCharsets.UTF_8));
+
+ String vp = new String(attributes.get(KEY_VENDOR_PRODUCT), StandardCharsets.UTF_8);
+ if (vp != null) {
+ String[] vpArray = vp.split("\\+");
+ if (vpArray.length > 0) {
+ this.vendorId = Long.parseLong(vpArray[0]);
+ if (vpArray.length == 2) {
+ this.productId = Long.parseLong(vpArray[1]);
+ }
+ }
+ }
+ }
+
+ public String getHostName() {
+ return hostName;
+ }
+
+ public void setHostName(String hostName) {
+ this.hostName = hostName;
+ }
+
+ public String getInstanceName() {
+ return instanceName;
+ }
+
+ public void setInstanceName(String instanceName) {
+ this.instanceName = instanceName;
+ }
+
+ public long getLongDiscriminator() {
+ return longDiscriminator;
+ }
+
+ public void setLongDiscriminator(long longDiscriminator) {
+ this.longDiscriminator = longDiscriminator;
+ }
+
+ public long getVendorId() {
+ return vendorId;
+ }
+
+ public void setVendorId(long vendorId) {
+ this.vendorId = vendorId;
+ }
+
+ public long getProductId() {
+ return productId;
+ }
+
+ public void setProductId(long productId) {
+ this.productId = productId;
+ }
+
+ public byte getCommissioningMode() {
+ return commissioningMode;
+ }
+
+ public void setCommissioningMode(byte commissioningMode) {
+ this.commissioningMode = commissioningMode;
+ }
+
+ public long getDeviceType() {
+ return deviceType;
+ }
+
+ public void setDeviceType(long deviceType) {
+ this.deviceType = deviceType;
+ }
+
+ public String getDeviceName() {
+ return deviceName;
+ }
+
+ public void setDeviceName(String deviceName) {
+ this.deviceName = deviceName;
+ }
+
+ public byte[] getRotatingId() {
+ return rotatingId;
+ }
+
+ public void setRotatingId(byte[] rotatingId) {
+ this.rotatingId = rotatingId;
+ }
+
+ public int getRotatingIdLen() {
+ return rotatingIdLen;
+ }
+
+ public void setRotatingIdLen(int rotatingIdLen) {
+ this.rotatingIdLen = rotatingIdLen;
+ }
+
+ public short getPairingHint() {
+ return pairingHint;
+ }
+
+ public void setPairingHint(short pairingHint) {
+ this.pairingHint = pairingHint;
+ }
+
+ public String getPairingInstruction() {
+ return pairingInstruction;
+ }
+
+ public void setPairingInstruction(String pairingInstruction) {
+ this.pairingInstruction = pairingInstruction;
+ }
+
+ public short getPort() {
+ return port;
+ }
+
+ public void setPort(short port) {
+ this.port = port;
+ }
+
+ public int getNumIPs() {
+ return numIPs;
+ }
+
+ public void setNumIPs(int numIPs) {
+ this.numIPs = numIPs;
+ }
+
+ public List getIpAddresses() {
+ return ipAddresses;
+ }
+
+ public void setIpAddresses(List ipAddresses) {
+ this.ipAddresses = ipAddresses;
+ }
+
+ @Override
+ public String toString() {
+ return "DiscoveredNodeData{"
+ + "hostName='"
+ + hostName
+ + '\''
+ + ", instanceName='"
+ + instanceName
+ + '\''
+ + ", longDiscriminator="
+ + longDiscriminator
+ + ", vendorId="
+ + vendorId
+ + ", productId="
+ + productId
+ + ", commissioningMode="
+ + commissioningMode
+ + ", deviceType="
+ + deviceType
+ + ", deviceName='"
+ + deviceName
+ + '\''
+ + ", rotatingId="
+ + Arrays.toString(rotatingId)
+ + ", rotatingIdLen="
+ + rotatingIdLen
+ + ", pairingHint="
+ + pairingHint
+ + ", pairingInstruction='"
+ + pairingInstruction
+ + '\''
+ + ", port="
+ + port
+ + ", numIPs="
+ + numIPs
+ + ", ipAddresses="
+ + ipAddresses
+ + '}';
+ }
+}
diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/util/GlobalCastingConstants.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/util/GlobalCastingConstants.java
new file mode 100644
index 00000000000000..f6e60ec63a5b67
--- /dev/null
+++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/util/GlobalCastingConstants.java
@@ -0,0 +1,5 @@
+package com.chip.casting.util;
+
+public class GlobalCastingConstants {
+ public static final String CommissionerServiceType = "_matterd._udp.";
+}
diff --git a/examples/tv-casting-app/android/App/app/src/main/res/layout/activity_main.xml b/examples/tv-casting-app/android/App/app/src/main/res/layout/activity_main.xml
index 99d71254d7b2e9..62a1aa33d57f63 100644
--- a/examples/tv-casting-app/android/App/app/src/main/res/layout/activity_main.xml
+++ b/examples/tv-casting-app/android/App/app/src/main/res/layout/activity_main.xml
@@ -4,14 +4,13 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".MainActivity">
+ tools:context="MainActivity">
-
-
-
\ No newline at end of file
+
+
diff --git a/examples/tv-casting-app/android/App/app/src/test/java/com/chip/casting/ExampleUnitTest.java b/examples/tv-casting-app/android/App/app/src/test/java/com/chip/casting/ExampleUnitTest.java
deleted file mode 100644
index 9c9e4486d25319..00000000000000
--- a/examples/tv-casting-app/android/App/app/src/test/java/com/chip/casting/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.chip.casting;
-
-import static org.junit.Assert.*;
-
-import org.junit.Test;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
-}
diff --git a/examples/tv-casting-app/android/App/app/src/test/java/com/chip/casting/dnssd/CommissionerDiscoveryListenerTest.java b/examples/tv-casting-app/android/App/app/src/test/java/com/chip/casting/dnssd/CommissionerDiscoveryListenerTest.java
new file mode 100644
index 00000000000000..7c29fdfa9db03e
--- /dev/null
+++ b/examples/tv-casting-app/android/App/app/src/test/java/com/chip/casting/dnssd/CommissionerDiscoveryListenerTest.java
@@ -0,0 +1,68 @@
+package com.chip.casting.dnssd;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import com.chip.casting.app.CastingContext;
+import com.chip.casting.util.GlobalCastingConstants;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CommissionerDiscoveryListenerTest {
+ @Mock private CastingContext castingContext;
+
+ @Mock private NsdServiceInfo nsdService;
+
+ @Mock private NsdManager nsdManager;
+
+ @Test
+ public void onServiceFound_callsResolve_whenCommissionerDiscovered() {
+ when(nsdService.getServiceType()).thenReturn(GlobalCastingConstants.CommissionerServiceType);
+ when(castingContext.getNsdManager()).thenReturn(nsdManager);
+ doNothing().when(nsdManager).resolveService(any(), any());
+
+ CommissionerDiscoveryListener listener = new CommissionerDiscoveryListener(castingContext);
+ listener.onServiceFound(nsdService);
+
+ verify(nsdManager, times(1)).resolveService(any(), any());
+ }
+
+ @Test
+ public void onServiceFound_noCallsToResolve_whenNonCommissionerDiscovered() {
+ when(nsdService.getServiceType()).thenReturn("_type._udp");
+ CommissionerDiscoveryListener listener = new CommissionerDiscoveryListener(castingContext);
+ listener.onServiceFound(nsdService);
+
+ verify(nsdManager, times(0)).resolveService(any(), any());
+ }
+
+ @Test
+ public void onStartDiscoveryFailed_callsStopServiceDiscovery() {
+ when(castingContext.getNsdManager()).thenReturn(nsdManager);
+ doNothing().when(nsdManager).stopServiceDiscovery(any());
+
+ CommissionerDiscoveryListener listener = new CommissionerDiscoveryListener(castingContext);
+ listener.onStartDiscoveryFailed("_test_type._udp", 1);
+
+ verify(nsdManager, times(1)).stopServiceDiscovery(any());
+ }
+
+ @Test
+ public void onStopDiscoveryFailed_callsStopServiceDiscovery() {
+ when(castingContext.getNsdManager()).thenReturn(nsdManager);
+ doNothing().when(nsdManager).stopServiceDiscovery(any());
+
+ CommissionerDiscoveryListener listener = new CommissionerDiscoveryListener(castingContext);
+ listener.onStopDiscoveryFailed("_test_type._udp", 1);
+
+ verify(nsdManager, times(1)).stopServiceDiscovery(any());
+ }
+}