[Android] Fix discover Commissionable Device API issue when discovered multiple devices (#32459)

* Fix Android Commissionable Device API when searched multiple devices

* Fix Android Commissionable Device API when searched multiple devices

* Fix from comment, fix edge case issue

* Restyled by google-java-format

---------

Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt
index 6372b14..f8686ce 100644
--- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt
+++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt
@@ -74,7 +74,10 @@
           AndroidBleManager(context),
           PreferencesKeyValueStoreManager(context),
           PreferencesConfigurationManager(context),
-          NsdManagerServiceResolver(context),
+          NsdManagerServiceResolver(
+            context,
+            NsdManagerServiceResolver.NsdManagerResolverAvailState()
+          ),
           NsdManagerServiceBrowser(context),
           ChipMdnsCallbackImpl(),
           DiagnosticDataProviderImpl(context)
diff --git a/src/controller/AbstractDnssdDiscoveryController.cpp b/src/controller/AbstractDnssdDiscoveryController.cpp
index d0f6d5d..2443c5f 100644
--- a/src/controller/AbstractDnssdDiscoveryController.cpp
+++ b/src/controller/AbstractDnssdDiscoveryController.cpp
@@ -35,7 +35,8 @@
             continue;
         }
         if (strcmp(discoveredNode.resolutionData.hostName, nodeData.resolutionData.hostName) == 0 &&
-            discoveredNode.resolutionData.port == nodeData.resolutionData.port)
+            discoveredNode.resolutionData.port == nodeData.resolutionData.port &&
+            discoveredNode.resolutionData.ipAddress == nodeData.resolutionData.ipAddress)
         {
             discoveredNode = nodeData;
             if (mDeviceDiscoveryDelegate != nullptr)
diff --git a/src/platform/android/java/chip/platform/NsdManagerServiceBrowser.java b/src/platform/android/java/chip/platform/NsdManagerServiceBrowser.java
index 3532182..f98acdf 100644
--- a/src/platform/android/java/chip/platform/NsdManagerServiceBrowser.java
+++ b/src/platform/android/java/chip/platform/NsdManagerServiceBrowser.java
@@ -34,10 +34,19 @@
   private final NsdManager nsdManager;
   private MulticastLock multicastLock;
   private Handler mainThreadHandler;
+  private final long timeout;
 
   private HashMap<Long, NsdManagerDiscovery> callbackMap;
 
   public NsdManagerServiceBrowser(Context context) {
+    this(context, BROWSE_SERVICE_TIMEOUT);
+  }
+
+  /**
+   * @param context application context
+   * @param timeout Timeout value in case there is no response after calling browse
+   */
+  public NsdManagerServiceBrowser(Context context, long timeout) {
     this.nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
     this.mainThreadHandler = new Handler(Looper.getMainLooper());
 
@@ -46,6 +55,7 @@
             .createMulticastLock("chipBrowseMulticastLock");
     this.multicastLock.setReferenceCounted(true);
     callbackMap = new HashMap<>();
+    this.timeout = timeout;
   }
 
   @Override
@@ -62,7 +72,7 @@
           }
         };
     startDiscover(serviceType, callbackHandle, contextHandle, chipMdnsCallback);
-    mainThreadHandler.postDelayed(timeoutRunnable, BROWSE_SERVICE_TIMEOUT);
+    mainThreadHandler.postDelayed(timeoutRunnable, timeout);
   }
 
   public void startDiscover(
diff --git a/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java b/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java
index c018551..1f1a9ef 100644
--- a/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java
+++ b/src/platform/android/java/chip/platform/NsdManagerServiceResolver.java
@@ -22,13 +22,14 @@
 import android.net.nsd.NsdServiceInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.MulticastLock;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.Log;
 import androidx.annotation.Nullable;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -38,30 +39,38 @@
   private static final long RESOLVE_SERVICE_TIMEOUT = 30000;
   private final NsdManager nsdManager;
   private MulticastLock multicastLock;
-  private Handler mainThreadHandler;
   private List<NsdManager.RegistrationListener> registrationListeners = new ArrayList<>();
   private final CopyOnWriteArrayList<String> mMFServiceName = new CopyOnWriteArrayList<>();
   @Nullable private final NsdManagerResolverAvailState nsdManagerResolverAvailState;
+  private final long timeout;
 
   /**
    * @param context application context
    * @param nsdManagerResolverAvailState Passing NsdManagerResolverAvailState allows
    *     NsdManagerServiceResolver to synchronize on the usage of NsdManager's resolveService() API
+   * @param timeout Timeout value in case there is no response after calling resolve
    */
   public NsdManagerServiceResolver(
-      Context context, @Nullable NsdManagerResolverAvailState nsdManagerResolverAvailState) {
+      Context context,
+      @Nullable NsdManagerResolverAvailState nsdManagerResolverAvailState,
+      long timeout) {
     this.nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
-    this.mainThreadHandler = new Handler(Looper.getMainLooper());
 
     this.multicastLock =
         ((WifiManager) context.getSystemService(Context.WIFI_SERVICE))
             .createMulticastLock("chipMulticastLock");
     this.multicastLock.setReferenceCounted(true);
     this.nsdManagerResolverAvailState = nsdManagerResolverAvailState;
+    this.timeout = timeout;
   }
 
   public NsdManagerServiceResolver(Context context) {
-    this(context, null);
+    this(context, null, RESOLVE_SERVICE_TIMEOUT);
+  }
+
+  public NsdManagerServiceResolver(
+      Context context, @Nullable NsdManagerResolverAvailState nsdManagerResolverAvailState) {
+    this(context, nsdManagerResolverAvailState, RESOLVE_SERVICE_TIMEOUT);
   }
 
   @Override
@@ -82,6 +91,10 @@
             + serviceType
             + "'");
 
+    if (nsdManagerResolverAvailState != null) {
+      nsdManagerResolverAvailState.acquireResolver();
+    }
+
     Runnable timeoutRunnable =
         new Runnable() {
           @Override
@@ -92,14 +105,18 @@
             Log.d(TAG, "resolve: Timing out");
             if (multicastLock.isHeld()) {
               multicastLock.release();
+            }
 
-              if (nsdManagerResolverAvailState != null) {
-                nsdManagerResolverAvailState.signalFree();
-              }
+            if (nsdManagerResolverAvailState != null) {
+              nsdManagerResolverAvailState.signalFree();
             }
           }
         };
 
+    ScheduledFuture<?> resolveTimeoutExecutor =
+        Executors.newSingleThreadScheduledExecutor()
+            .schedule(timeoutRunnable, timeout, TimeUnit.MILLISECONDS);
+
     NsdServiceFinderAndResolver serviceFinderResolver =
         new NsdServiceFinderAndResolver(
             this.nsdManager,
@@ -107,13 +124,10 @@
             callbackHandle,
             contextHandle,
             chipMdnsCallback,
-            timeoutRunnable,
             multicastLock,
-            mainThreadHandler,
+            resolveTimeoutExecutor,
             nsdManagerResolverAvailState);
     serviceFinderResolver.start();
-
-    mainThreadHandler.postDelayed(timeoutRunnable, RESOLVE_SERVICE_TIMEOUT);
   }
 
   @Override
diff --git a/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java b/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java
index fc3425f..70ffdbc 100644
--- a/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java
+++ b/src/platform/android/java/chip/platform/NsdServiceFinderAndResolver.java
@@ -21,7 +21,6 @@
 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;
@@ -38,9 +37,8 @@
   private final long callbackHandle;
   private final long contextHandle;
   private final ChipMdnsCallback chipMdnsCallback;
-  private final Runnable timeoutRunnable;
   private final MulticastLock multicastLock;
-  private final Handler mainThreadHandler;
+  private final ScheduledFuture<?> resolveTimeoutExecutor;
 
   @Nullable
   private final NsdManagerServiceResolver.NsdManagerResolverAvailState nsdManagerResolverAvailState;
@@ -53,18 +51,16 @@
       final long callbackHandle,
       final long contextHandle,
       final ChipMdnsCallback chipMdnsCallback,
-      final Runnable timeoutRunnable,
       final MulticastLock multicastLock,
-      final Handler mainThreadHandler,
+      final ScheduledFuture<?> resolveTimeoutExecutor,
       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.resolveTimeoutExecutor = resolveTimeoutExecutor;
     this.nsdManagerResolverAvailState = nsdManagerResolverAvailState;
   }
 
@@ -101,16 +97,9 @@
 
       if (stopDiscoveryRunnable.cancel(false)) {
         nsdManager.stopServiceDiscovery(this);
-        if (multicastLock.isHeld()) {
-          multicastLock.release();
-        }
       }
 
-      if (nsdManagerResolverAvailState != null) {
-        nsdManagerResolverAvailState.acquireResolver();
-      }
-
-      resolveService(service, callbackHandle, contextHandle, chipMdnsCallback, timeoutRunnable);
+      resolveService(service, callbackHandle, contextHandle, chipMdnsCallback);
     } else {
       Log.d(TAG, "onServiceFound: found service not a target for resolution, ignoring " + service);
     }
@@ -120,8 +109,7 @@
       NsdServiceInfo serviceInfo,
       final long callbackHandle,
       final long contextHandle,
-      final ChipMdnsCallback chipMdnsCallback,
-      Runnable timeoutRunnable) {
+      final ChipMdnsCallback chipMdnsCallback) {
     this.nsdManager.resolveService(
         serviceInfo,
         new NsdManager.ResolveListener() {
@@ -152,7 +140,7 @@
                 nsdManagerResolverAvailState.signalFree();
               }
             }
-            mainThreadHandler.removeCallbacks(timeoutRunnable);
+            resolveTimeoutExecutor.cancel(false);
           }
 
           @Override
@@ -188,7 +176,7 @@
                 nsdManagerResolverAvailState.signalFree();
               }
             }
-            mainThreadHandler.removeCallbacks(timeoutRunnable);
+            resolveTimeoutExecutor.cancel(false);
           }
         });
   }