[Android] Implement LIT ICD for subscription (#33152)

* Implement android LIT ICD subscribe

* Refactoring ChipICDClient

* Fix fabric Index, remoteID issue

* Restyled by whitespace

* Restyled by google-java-format

* Restyled by clang-format

* Restyled by gn

* remove Unused API

* Add Kotlin ICD Client Info

* Fix build error

---------

Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/scripts/py_matter_idl/matter_idl/generators/java/ChipClusters_java.jinja b/scripts/py_matter_idl/matter_idl/generators/java/ChipClusters_java.jinja
index b391d09..954bd7d 100644
--- a/scripts/py_matter_idl/matter_idl/generators/java/ChipClusters_java.jinja
+++ b/scripts/py_matter_idl/matter_idl/generators/java/ChipClusters_java.jinja
@@ -219,7 +219,9 @@
         int maxInterval) {
       ReportCallbackJni jniCallback = new ReportCallbackJni(callback, callback, callback);
       ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, attributeId);
-      ChipInteractionClient.subscribe(0, jniCallback.getCallbackHandle(), devicePtr, Arrays.asList(path), null, null, minInterval, maxInterval, false, true, timeoutMillis.orElse(0L).intValue(), null);
+      int fabricIndex = ChipInteractionClient.getFabricIndex(devicePtr);
+      long deviceId = ChipInteractionClient.getRemoteDeviceId(devicePtr);
+      ChipInteractionClient.subscribe(0, jniCallback.getCallbackHandle(), devicePtr, Arrays.asList(path), null, null, minInterval, maxInterval, false, true, timeoutMillis.orElse(0L).intValue(), null, ChipICDClient.isPeerICDClient(fabricIndex, deviceId));
     }
 
     protected void invoke(
diff --git a/scripts/py_matter_idl/matter_idl/tests/outputs/several_clusters/java/ChipClusters.java b/scripts/py_matter_idl/matter_idl/tests/outputs/several_clusters/java/ChipClusters.java
index b574271..037870e 100644
--- a/scripts/py_matter_idl/matter_idl/tests/outputs/several_clusters/java/ChipClusters.java
+++ b/scripts/py_matter_idl/matter_idl/tests/outputs/several_clusters/java/ChipClusters.java
@@ -142,7 +142,9 @@
         int maxInterval) {
       ReportCallbackJni jniCallback = new ReportCallbackJni(callback, callback, callback);
       ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, attributeId);
-      ChipInteractionClient.subscribe(0, jniCallback.getCallbackHandle(), devicePtr, Arrays.asList(path), null, null, minInterval, maxInterval, false, true, timeoutMillis.orElse(0L).intValue(), null);
+      int fabricIndex = ChipInteractionClient.getFabricIndex(devicePtr);
+      long deviceId = ChipInteractionClient.getRemoteDeviceId(devicePtr);
+      ChipInteractionClient.subscribe(0, jniCallback.getCallbackHandle(), devicePtr, Arrays.asList(path), null, null, minInterval, maxInterval, false, true, timeoutMillis.orElse(0L).intValue(), null, ChipICDClient.isPeerICDClient(fabricIndex, deviceId));
     }
 
     protected void invoke(
diff --git a/src/controller/java/AndroidDeviceControllerWrapper.cpp b/src/controller/java/AndroidDeviceControllerWrapper.cpp
index c5b3863..1576e58 100644
--- a/src/controller/java/AndroidDeviceControllerWrapper.cpp
+++ b/src/controller/java/AndroidDeviceControllerWrapper.cpp
@@ -30,6 +30,7 @@
 #include <lib/support/JniTypeWrappers.h>
 
 #include <controller/CHIPDeviceControllerFactory.h>
+#include <controller/java/AndroidICDClient.h>
 #include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h>
 #include <credentials/attestation_verifier/DeviceAttestationVerifier.h>
 #include <lib/core/TLV.h>
@@ -185,7 +186,7 @@
         chip::Credentials::SetDeviceAttestationVerifier(GetDefaultDACVerifier(testingRootStore));
     }
 
-    *errInfoOnFailure = wrapper->mICDClientStorage.Init(wrapperStorage, &wrapper->mSessionKeystore);
+    *errInfoOnFailure = getICDClientStorage()->Init(wrapperStorage, &wrapper->mSessionKeystore);
     if (*errInfoOnFailure != CHIP_NO_ERROR)
     {
         ChipLogError(Controller, "ICD Client Storage failure");
@@ -401,12 +402,12 @@
     *errInfoOnFailure = chip::Credentials::SetSingleIpkEpochKey(
         &wrapper->mGroupDataProvider, wrapper->Controller()->GetFabricIndex(), ipkSpan, compressedFabricIdSpan);
 
-    wrapper->getICDClientStorage()->UpdateFabricList(wrapper->Controller()->GetFabricIndex());
+    getICDClientStorage()->UpdateFabricList(wrapper->Controller()->GetFabricIndex());
 
     auto engine       = chip::app::InteractionModelEngine::GetInstance();
-    *errInfoOnFailure = wrapper->mCheckInDelegate.Init(&wrapper->mICDClientStorage, engine);
+    *errInfoOnFailure = wrapper->mCheckInDelegate.Init(getICDClientStorage(), engine);
     *errInfoOnFailure = wrapper->mCheckInHandler.Init(DeviceControllerFactory::GetInstance().GetSystemState()->ExchangeMgr(),
-                                                      &wrapper->mICDClientStorage, &wrapper->mCheckInDelegate, engine);
+                                                      getICDClientStorage(), &wrapper->mCheckInDelegate, engine);
 
     memset(ipkBuffer.data(), 0, ipkBuffer.size());
 
@@ -725,7 +726,7 @@
 
     if (error != CHIP_NO_ERROR && mDeviceIsICD)
     {
-        CHIP_ERROR deleteEntryError = mICDClientStorage.DeleteEntry(ScopedNodeId(deviceId, Controller()->GetFabricIndex()));
+        CHIP_ERROR deleteEntryError = getICDClientStorage()->DeleteEntry(ScopedNodeId(deviceId, Controller()->GetFabricIndex()));
         if (deleteEntryError != CHIP_NO_ERROR)
         {
             ChipLogError(chipTool, "Failed to delete ICD entry: %" CHIP_ERROR_FORMAT, deleteEntryError.Format());
@@ -1003,10 +1004,10 @@
 
     ByteSpan symmetricKey = mAutoCommissioner.GetCommissioningParameters().GetICDSymmetricKey().Value();
 
-    err = mICDClientStorage.SetKey(clientInfo, symmetricKey);
+    err = getICDClientStorage()->SetKey(clientInfo, symmetricKey);
     if (err == CHIP_NO_ERROR)
     {
-        err = mICDClientStorage.StoreEntry(clientInfo);
+        err = getICDClientStorage()->StoreEntry(clientInfo);
     }
 
     if (err == CHIP_NO_ERROR)
@@ -1015,7 +1016,7 @@
     }
     else
     {
-        mICDClientStorage.RemoveKey(clientInfo);
+        getICDClientStorage()->RemoveKey(clientInfo);
         ChipLogError(Controller, "Failed to persist symmetric key for " ChipLogFormatX64 ": %s", ChipLogValueX64(icdNodeId),
                      err.AsString());
     }
diff --git a/src/controller/java/AndroidDeviceControllerWrapper.h b/src/controller/java/AndroidDeviceControllerWrapper.h
index 3acf169..51beae2 100644
--- a/src/controller/java/AndroidDeviceControllerWrapper.h
+++ b/src/controller/java/AndroidDeviceControllerWrapper.h
@@ -212,8 +212,6 @@
 
     CHIP_ERROR FinishOTAProvider();
 
-    chip::app::DefaultICDClientStorage * getICDClientStorage() { return &mICDClientStorage; }
-
     CHIP_ERROR SetICDCheckInDelegate(jobject checkInDelegate);
 
 private:
@@ -228,7 +226,6 @@
     // TODO: This may need to be injected as a SessionKeystore*
     chip::Crypto::RawKeySessionKeystore mSessionKeystore;
 
-    chip::app::DefaultICDClientStorage mICDClientStorage;
     chip::app::AndroidCheckInDelegate mCheckInDelegate;
     chip::app::CheckInHandler mCheckInHandler;
 
diff --git a/src/controller/java/AndroidICDClient.cpp b/src/controller/java/AndroidICDClient.cpp
new file mode 100644
index 0000000..83cb522
--- /dev/null
+++ b/src/controller/java/AndroidICDClient.cpp
@@ -0,0 +1,99 @@
+/*
+ *   Copyright (c) 2024 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.
+ *
+ */
+
+/**
+ *    @file
+ *      Implementation of ICD Client API for Android Platform
+ *
+ */
+
+#include "AndroidICDClient.h"
+
+#include <app/icd/client/ICDClientInfo.h>
+
+chip::app::DefaultICDClientStorage sICDClientStorage;
+
+jobject getICDClientInfo(JNIEnv * env, const char * icdClientInfoSign, jint jFabricIndex)
+{
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    jobject jInfo = nullptr;
+    chip::app::ICDClientInfo info;
+    chip::FabricIndex fabricIndex = static_cast<chip::FabricIndex>(jFabricIndex);
+
+    ChipLogProgress(Controller, "getICDClientInfo(%u) called", fabricIndex);
+
+    err = chip::JniReferences::GetInstance().CreateArrayList(jInfo);
+    VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
+                        ChipLogError(Controller, "CreateArrayList failed!: %" CHIP_ERROR_FORMAT, err.Format()));
+
+    auto iter = getICDClientStorage()->IterateICDClientInfo();
+    VerifyOrReturnValue(iter != nullptr, nullptr, ChipLogError(Controller, "IterateICDClientInfo failed!"));
+    chip::app::DefaultICDClientStorage::ICDClientInfoIteratorWrapper clientInfoIteratorWrapper(iter);
+
+    jmethodID constructor;
+    jclass infoClass;
+    chip::JniLocalReferenceScope scope(env);
+
+    err = chip::JniReferences::GetInstance().GetLocalClassRef(env, icdClientInfoSign, infoClass);
+    VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
+                        ChipLogError(Controller, "Find ICDClientInfo class: %" CHIP_ERROR_FORMAT, err.Format()));
+
+    env->ExceptionClear();
+    constructor = env->GetMethodID(infoClass, "<init>", "(JJJJ[B[B)V");
+    VerifyOrReturnValue(constructor != nullptr, nullptr, ChipLogError(Controller, "Find GetMethodID error!"));
+
+    while (iter->Next(info))
+    {
+        jbyteArray jIcdAesKey  = nullptr;
+        jbyteArray jIcdHmacKey = nullptr;
+        jobject jICDClientInfo = nullptr;
+
+        if (info.peer_node.GetFabricIndex() != fabricIndex)
+        {
+            continue;
+        }
+
+        err = chip::JniReferences::GetInstance().N2J_ByteArray(env,
+                                                               info.aes_key_handle.As<chip::Crypto::Symmetric128BitsKeyByteArray>(),
+                                                               chip::Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, jIcdAesKey);
+        VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
+                            ChipLogError(Controller, "ICD AES KEY N2J_ByteArray error!: %" CHIP_ERROR_FORMAT, err.Format()));
+
+        err = chip::JniReferences::GetInstance().N2J_ByteArray(
+            env, info.hmac_key_handle.As<chip::Crypto::Symmetric128BitsKeyByteArray>(),
+            chip::Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, jIcdHmacKey);
+        VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
+                            ChipLogError(Controller, "ICD HMAC KEY N2J_ByteArray error!: %" CHIP_ERROR_FORMAT, err.Format()));
+
+        jICDClientInfo = (jobject) env->NewObject(infoClass, constructor, static_cast<jlong>(info.peer_node.GetNodeId()),
+                                                  static_cast<jlong>(info.start_icd_counter), static_cast<jlong>(info.offset),
+                                                  static_cast<jlong>(info.monitored_subject), jIcdAesKey, jIcdHmacKey);
+
+        err = chip::JniReferences::GetInstance().AddToList(jInfo, jICDClientInfo);
+        VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
+                            ChipLogError(Controller, "AddToList error!: %" CHIP_ERROR_FORMAT, err.Format()));
+    }
+
+    return jInfo;
+}
+
+chip::app::DefaultICDClientStorage * getICDClientStorage()
+{
+    return &sICDClientStorage;
+}
diff --git a/src/controller/java/AndroidICDClient.h b/src/controller/java/AndroidICDClient.h
new file mode 100644
index 0000000..e5b929e
--- /dev/null
+++ b/src/controller/java/AndroidICDClient.h
@@ -0,0 +1,32 @@
+/*
+ *   Copyright (c) 2024 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.
+ *
+ */
+
+/**
+ *    @file
+ *      Implementation of ICD Client API for Android Platform
+ *
+ */
+
+#pragma once
+
+#include <app/icd/client/DefaultICDClientStorage.h>
+#include <lib/support/JniReferences.h>
+
+jobject getICDClientInfo(JNIEnv * env, const char * icdClientInfoSign, jint jFabricIndex);
+
+chip::app::DefaultICDClientStorage * getICDClientStorage();
diff --git a/src/controller/java/AndroidInteractionClient.cpp b/src/controller/java/AndroidInteractionClient.cpp
index 6ab01bc..84506fd 100644
--- a/src/controller/java/AndroidInteractionClient.cpp
+++ b/src/controller/java/AndroidInteractionClient.cpp
@@ -46,7 +46,7 @@
 
 CHIP_ERROR subscribe(JNIEnv * env, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributePathList,
                      jobject eventPathList, jobject dataVersionFilterList, jint minInterval, jint maxInterval,
-                     jboolean keepSubscriptions, jboolean isFabricFiltered, jint imTimeoutMs, jobject eventMin)
+                     jboolean keepSubscriptions, jboolean isFabricFiltered, jint imTimeoutMs, jobject eventMin, jboolean isPeerLIT)
 {
     chip::DeviceLayer::StackLock lock;
     CHIP_ERROR err               = CHIP_NO_ERROR;
@@ -128,6 +128,9 @@
         params.mEventNumber.SetValue(static_cast<chip::EventNumber>(JniReferences::GetInstance().LongToPrimitive(eventMin)));
     }
 
+    params.mIsPeerLIT = (isPeerLIT == JNI_TRUE);
+    ChipLogProgress(Controller, "Peer ICD type is set to %s", params.mIsPeerLIT ? "LIT-ICD" : "non LIT-ICD");
+
     if (eventPathList != nullptr)
     {
         jint jNumEventPaths = 0;
@@ -804,6 +807,30 @@
     return err;
 }
 
+jlong getRemoteDeviceId(jlong devicePtr)
+{
+    OperationalDeviceProxy * chipDevice = reinterpret_cast<OperationalDeviceProxy *>(devicePtr);
+    if (chipDevice == nullptr)
+    {
+        ChipLogProgress(Controller, "Could not cast device pointer to Device object");
+        return static_cast<jlong>(chip::kUndefinedNodeId);
+    }
+
+    return static_cast<jlong>(chipDevice->GetDeviceId());
+}
+
+jint getFabricIndex(jlong devicePtr)
+{
+    OperationalDeviceProxy * chipDevice = reinterpret_cast<OperationalDeviceProxy *>(devicePtr);
+    if (chipDevice == nullptr)
+    {
+        ChipLogProgress(Controller, "Could not cast device pointer to Device object");
+        return static_cast<jint>(chip::kUndefinedFabricIndex);
+    }
+
+    return static_cast<jint>(chipDevice->GetPeerScopedNodeId().GetFabricIndex());
+}
+
 /**
  * Takes objects in attributePathList, converts them to app:AttributePathParams, and appends them to outAttributePathParamsList.
  */
diff --git a/src/controller/java/AndroidInteractionClient.h b/src/controller/java/AndroidInteractionClient.h
index 32ee629..646ac14 100644
--- a/src/controller/java/AndroidInteractionClient.h
+++ b/src/controller/java/AndroidInteractionClient.h
@@ -22,7 +22,7 @@
 
 CHIP_ERROR subscribe(JNIEnv * env, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributePathList,
                      jobject eventPathList, jobject dataVersionFilterList, jint minInterval, jint maxInterval,
-                     jboolean keepSubscriptions, jboolean isFabricFiltered, jint imTimeoutMs, jobject eventMin);
+                     jboolean keepSubscriptions, jboolean isFabricFiltered, jint imTimeoutMs, jobject eventMin, jboolean isPeerLIT);
 CHIP_ERROR read(JNIEnv * env, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributePathList, jobject eventPathList,
                 jobject dataVersionFilterList, jboolean isFabricFiltered, jint imTimeoutMs, jobject eventMin);
 CHIP_ERROR write(JNIEnv * env, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributeList,
@@ -32,3 +32,6 @@
 CHIP_ERROR extendableInvoke(JNIEnv * env, jlong handle, jlong callbackHandle, jlong devicePtr, jobject invokeElementList,
                             jint timedRequestTimeoutMs, jint imTimeoutMs);
 CHIP_ERROR shutdownSubscriptions(JNIEnv * env, jlong handle, jobject fabricIndex, jobject peerNodeId, jobject subscriptionId);
+
+jlong getRemoteDeviceId(jlong devicePtr);
+jint getFabricIndex(jlong devicePtr);
diff --git a/src/controller/java/BUILD.gn b/src/controller/java/BUILD.gn
index 129ebe6..e22c50c 100644
--- a/src/controller/java/BUILD.gn
+++ b/src/controller/java/BUILD.gn
@@ -51,11 +51,14 @@
     "AndroidConnectionFailureExceptions.h",
     "AndroidControllerExceptions.cpp",
     "AndroidControllerExceptions.h",
+    "AndroidICDClient.cpp",
+    "AndroidICDClient.h",
     "AndroidInteractionClient.cpp",
     "AndroidInteractionClient.h",
     "BaseCHIPCluster-JNI.cpp",
     "CHIPAttributeTLVValueDecoder.h",
     "CHIPEventTLVValueDecoder.h",
+    "CHIPICDClient-JNI.cpp",
     "CHIPInteractionClient-JNI.cpp",
     "CHIPInteractionClient-JNI.h",
   ]
@@ -70,6 +73,7 @@
   }
 
   deps = [
+    "${chip_root}/src/app/icd/client:manager",
     "${chip_root}/src/lib",
     "${chip_root}/src/lib/support/jsontlv",
     "${chip_root}/src/platform",
@@ -136,6 +140,7 @@
     "DeviceAttestationDelegateBridge.h",
     "GroupDeviceProxy.h",
     "MatterCallbacks-JNI.cpp",
+    "MatterICDClient-JNI.cpp",
     "MatterInteractionClient-JNI.cpp",
   ]
 
@@ -431,12 +436,14 @@
   sources = [
     "src/matter/controller/CompletionListenerAdapter.kt",
     "src/matter/controller/ControllerParams.kt",
+    "src/matter/controller/ICDClientInfo.kt",
     "src/matter/controller/InteractionClient.kt",
     "src/matter/controller/InvokeCallback.kt",
     "src/matter/controller/InvokeCallbackJni.kt",
     "src/matter/controller/MatterController.kt",
     "src/matter/controller/MatterControllerException.kt",
     "src/matter/controller/MatterControllerImpl.kt",
+    "src/matter/controller/MatterICDClientImpl.kt",
     "src/matter/controller/Messages.kt",
     "src/matter/controller/OperationalKeyConfig.kt",
     "src/matter/controller/ReportCallback.kt",
@@ -499,10 +506,12 @@
   sources = [
     "src/chip/devicecontroller/ChipClusterException.java",
     "src/chip/devicecontroller/ChipDeviceControllerException.java",
+    "src/chip/devicecontroller/ChipICDClient.java",
     "src/chip/devicecontroller/ChipInteractionClient.java",
     "src/chip/devicecontroller/ExtendableInvokeCallback.java",
     "src/chip/devicecontroller/ExtendableInvokeCallbackJni.java",
     "src/chip/devicecontroller/GetConnectedDeviceCallbackJni.java",
+    "src/chip/devicecontroller/ICDClientInfo.java",
     "src/chip/devicecontroller/InvokeCallback.java",
     "src/chip/devicecontroller/InvokeCallbackJni.java",
     "src/chip/devicecontroller/ReportCallback.java",
@@ -600,7 +609,6 @@
     "src/chip/devicecontroller/GroupKeySecurityPolicy.java",
     "src/chip/devicecontroller/ICDCheckInDelegate.java",
     "src/chip/devicecontroller/ICDCheckInDelegateWrapper.java",
-    "src/chip/devicecontroller/ICDClientInfo.java",
     "src/chip/devicecontroller/ICDDeviceInfo.java",
     "src/chip/devicecontroller/ICDRegistrationInfo.java",
     "src/chip/devicecontroller/KeypairDelegate.java",
diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp
index dd034ca..9b3d10a 100644
--- a/src/controller/java/CHIPDeviceController-JNI.cpp
+++ b/src/controller/java/CHIPDeviceController-JNI.cpp
@@ -2154,73 +2154,6 @@
     return javaCsr;
 }
 
-JNI_METHOD(jobject, getICDClientInfo)(JNIEnv * env, jobject self, jlong handle, jint jFabricIndex)
-{
-    chip::DeviceLayer::StackLock lock;
-
-    CHIP_ERROR err = CHIP_NO_ERROR;
-
-    jobject jInfo = nullptr;
-    chip::app::ICDClientInfo info;
-    chip::FabricIndex fabricIndex = static_cast<chip::FabricIndex>(jFabricIndex);
-
-    ChipLogProgress(Controller, "getICDClientInfo(%u) called", fabricIndex);
-    AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
-    VerifyOrReturnValue(wrapper != nullptr, nullptr, ChipLogError(Controller, "wrapper is null"));
-
-    err = JniReferences::GetInstance().CreateArrayList(jInfo);
-    VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
-                        ChipLogError(Controller, "CreateArrayList failed!: %" CHIP_ERROR_FORMAT, err.Format()));
-
-    auto iter = wrapper->getICDClientStorage()->IterateICDClientInfo();
-    VerifyOrReturnValue(iter != nullptr, nullptr, ChipLogError(Controller, "IterateICDClientInfo failed!"));
-    app::DefaultICDClientStorage::ICDClientInfoIteratorWrapper clientInfoIteratorWrapper(iter);
-
-    jmethodID constructor;
-    jclass infoClass;
-    JniLocalReferenceScope scope(env);
-
-    err = JniReferences::GetInstance().GetLocalClassRef(env, "chip/devicecontroller/ICDClientInfo", infoClass);
-    VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
-                        ChipLogError(Controller, "Find ICDClientInfo class: %" CHIP_ERROR_FORMAT, err.Format()));
-
-    env->ExceptionClear();
-    constructor = env->GetMethodID(infoClass, "<init>", "(JJJJ[B[B)V");
-    VerifyOrReturnValue(constructor != nullptr, nullptr, ChipLogError(Controller, "Find GetMethodID error!"));
-
-    while (iter->Next(info))
-    {
-        jbyteArray jIcdAesKey  = nullptr;
-        jbyteArray jIcdHmacKey = nullptr;
-        jobject jICDClientInfo = nullptr;
-
-        if (info.peer_node.GetFabricIndex() != fabricIndex)
-        {
-            continue;
-        }
-
-        err = chip::JniReferences::GetInstance().N2J_ByteArray(env, info.aes_key_handle.As<Crypto::Symmetric128BitsKeyByteArray>(),
-                                                               CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, jIcdAesKey);
-        VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
-                            ChipLogError(Controller, "ICD AES KEY N2J_ByteArray error!: %" CHIP_ERROR_FORMAT, err.Format()));
-
-        err = chip::JniReferences::GetInstance().N2J_ByteArray(env, info.hmac_key_handle.As<Crypto::Symmetric128BitsKeyByteArray>(),
-                                                               CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, jIcdHmacKey);
-        VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
-                            ChipLogError(Controller, "ICD HMAC KEY N2J_ByteArray error!: %" CHIP_ERROR_FORMAT, err.Format()));
-
-        jICDClientInfo = (jobject) env->NewObject(infoClass, constructor, static_cast<jlong>(info.peer_node.GetNodeId()),
-                                                  static_cast<jlong>(info.start_icd_counter), static_cast<jlong>(info.offset),
-                                                  static_cast<jlong>(info.monitored_subject), jIcdAesKey, jIcdHmacKey);
-
-        err = JniReferences::GetInstance().AddToList(jInfo, jICDClientInfo);
-        VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
-                            ChipLogError(Controller, "AddToList error!: %" CHIP_ERROR_FORMAT, err.Format()));
-    }
-
-    return jInfo;
-}
-
 void * IOThreadMain(void * arg)
 {
     JNIEnv * env;
diff --git a/src/controller/java/CHIPICDClient-JNI.cpp b/src/controller/java/CHIPICDClient-JNI.cpp
new file mode 100644
index 0000000..b1fb257
--- /dev/null
+++ b/src/controller/java/CHIPICDClient-JNI.cpp
@@ -0,0 +1,29 @@
+/*
+ *   Copyright (c) 2024 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.
+ *
+ */
+#include "AndroidICDClient.h"
+
+#include <platform/PlatformManager.h>
+
+#define JNI_METHOD(RETURN, METHOD_NAME) extern "C" JNIEXPORT RETURN JNICALL Java_chip_devicecontroller_ChipICDClient_##METHOD_NAME
+
+JNI_METHOD(jobject, getICDClientInfo)(JNIEnv * env, jobject self, jint jFabricIndex)
+{
+    chip::DeviceLayer::StackLock lock;
+
+    return getICDClientInfo(env, "chip/devicecontroller/ICDClientInfo", jFabricIndex);
+}
diff --git a/src/controller/java/CHIPInteractionClient-JNI.cpp b/src/controller/java/CHIPInteractionClient-JNI.cpp
index 62abf35..f6d8e0a 100644
--- a/src/controller/java/CHIPInteractionClient-JNI.cpp
+++ b/src/controller/java/CHIPInteractionClient-JNI.cpp
@@ -73,10 +73,10 @@
 JNI_METHOD(void, subscribe)
 (JNIEnv * env, jobject self, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributePathList, jobject eventPathList,
  jobject dataVersionFilterList, jint minInterval, jint maxInterval, jboolean keepSubscriptions, jboolean isFabricFiltered,
- jint imTimeoutMs, jobject eventMin)
+ jint imTimeoutMs, jobject eventMin, jboolean isPeerLIT)
 {
     CHIP_ERROR err = subscribe(env, handle, callbackHandle, devicePtr, attributePathList, eventPathList, dataVersionFilterList,
-                               minInterval, maxInterval, keepSubscriptions, isFabricFiltered, imTimeoutMs, eventMin);
+                               minInterval, maxInterval, keepSubscriptions, isFabricFiltered, imTimeoutMs, eventMin, isPeerLIT);
     if (err != CHIP_NO_ERROR)
     {
         ChipLogError(Controller, "JNI IM Subscribe Error: %" CHIP_ERROR_FORMAT, err.Format());
@@ -138,3 +138,15 @@
         ChipLogError(Controller, "Failed to shutdown subscriptions with Error: %" CHIP_ERROR_FORMAT, err.Format());
     }
 }
+
+JNI_METHOD(jlong, getRemoteDeviceId)
+(JNIEnv * env, jobject self, jlong devicePtr)
+{
+    return getRemoteDeviceId(devicePtr);
+}
+
+JNI_METHOD(jint, getFabricIndex)
+(JNIEnv * env, jobject self, jlong devicePtr)
+{
+    return getFabricIndex(devicePtr);
+}
diff --git a/src/controller/java/MatterICDClient-JNI.cpp b/src/controller/java/MatterICDClient-JNI.cpp
new file mode 100644
index 0000000..45ed4d9
--- /dev/null
+++ b/src/controller/java/MatterICDClient-JNI.cpp
@@ -0,0 +1,29 @@
+/*
+ *   Copyright (c) 2024 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.
+ *
+ */
+#include "AndroidICDClient.h"
+
+#include <platform/PlatformManager.h>
+
+#define JNI_METHOD(RETURN, METHOD_NAME) extern "C" JNIEXPORT RETURN JNICALL Java_matter_controller_MatterICDClientImpl_##METHOD_NAME
+
+JNI_METHOD(jobject, getICDClientInfo)(JNIEnv * env, jobject self, jint jFabricIndex)
+{
+    chip::DeviceLayer::StackLock lock;
+
+    return getICDClientInfo(env, "matter/controller/ICDClientInfo", jFabricIndex);
+}
diff --git a/src/controller/java/MatterInteractionClient-JNI.cpp b/src/controller/java/MatterInteractionClient-JNI.cpp
index 152e52a..5b526a3 100644
--- a/src/controller/java/MatterInteractionClient-JNI.cpp
+++ b/src/controller/java/MatterInteractionClient-JNI.cpp
@@ -22,10 +22,10 @@
 
 JNI_METHOD(void, subscribe)
 (JNIEnv * env, jobject self, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributePathList, jobject eventPathList,
- jint minInterval, jint maxInterval, jboolean keepSubscriptions, jboolean isFabricFiltered, jint imTimeoutMs)
+ jint minInterval, jint maxInterval, jboolean keepSubscriptions, jboolean isFabricFiltered, jint imTimeoutMs, jboolean isPeerLIT)
 {
     CHIP_ERROR err = subscribe(env, handle, callbackHandle, devicePtr, attributePathList, eventPathList, nullptr, minInterval,
-                               maxInterval, keepSubscriptions, isFabricFiltered, imTimeoutMs, nullptr);
+                               maxInterval, keepSubscriptions, isFabricFiltered, imTimeoutMs, nullptr, isPeerLIT);
     if (err != CHIP_NO_ERROR)
     {
         ChipLogError(Controller, "JNI IM Subscribe Error: %" CHIP_ERROR_FORMAT, err.Format());
@@ -77,3 +77,15 @@
         ChipLogError(Controller, "JNI IM Batch Invoke Error: %" CHIP_ERROR_FORMAT, err.Format());
     }
 }
+
+JNI_METHOD(jlong, getRemoteDeviceId)
+(JNIEnv * env, jobject self, jlong devicePtr)
+{
+    return getRemoteDeviceId(devicePtr);
+}
+
+JNI_METHOD(jint, getFabricIndex)
+(JNIEnv * env, jobject self, jlong devicePtr)
+{
+    return getFabricIndex(devicePtr);
+}
diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java
index 0844ffe..2e64d37 100644
--- a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java
+++ b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java
@@ -142,7 +142,9 @@
         int maxInterval) {
       ReportCallbackJni jniCallback = new ReportCallbackJni(callback, callback, callback);
       ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, attributeId);
-      ChipInteractionClient.subscribe(0, jniCallback.getCallbackHandle(), devicePtr, Arrays.asList(path), null, null, minInterval, maxInterval, false, true, timeoutMillis.orElse(0L).intValue(), null);
+      int fabricIndex = ChipInteractionClient.getFabricIndex(devicePtr);
+      long deviceId = ChipInteractionClient.getRemoteDeviceId(devicePtr);
+      ChipInteractionClient.subscribe(0, jniCallback.getCallbackHandle(), devicePtr, Arrays.asList(path), null, null, minInterval, maxInterval, false, true, timeoutMillis.orElse(0L).intValue(), null, ChipICDClient.isPeerICDClient(fabricIndex, deviceId));
     }
 
     protected void invoke(
diff --git a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
index 482bc2e..653cebf 100644
--- a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
+++ b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
@@ -743,7 +743,7 @@
   }
 
   public List<ICDClientInfo> getICDClientInfo() {
-    return getICDClientInfo(deviceControllerPtr, getFabricIndex(deviceControllerPtr));
+    return getICDClientInfo(getFabricIndex(deviceControllerPtr));
   }
 
   /**
@@ -752,7 +752,7 @@
    * @param fabricIndex the fabric index to check
    */
   public List<ICDClientInfo> getICDClientInfo(int fabricIndex) {
-    return getICDClientInfo(deviceControllerPtr, fabricIndex);
+    return ChipICDClient.getICDClientInfo(fabricIndex);
   }
 
   /* Shuts down all active subscriptions. */
@@ -838,7 +838,10 @@
         false,
         false,
         imTimeoutMs,
-        null);
+        null,
+        ChipICDClient.isPeerICDClient(
+            ChipInteractionClient.getFabricIndex(devicePtr),
+            ChipInteractionClient.getRemoteDeviceId(devicePtr)));
   }
 
   /**
@@ -876,7 +879,10 @@
         false,
         false,
         imTimeoutMs,
-        null);
+        null,
+        ChipICDClient.isPeerICDClient(
+            ChipInteractionClient.getFabricIndex(devicePtr),
+            ChipInteractionClient.getRemoteDeviceId(devicePtr)));
   }
 
   public void subscribeToEventPath(
@@ -902,7 +908,10 @@
         false,
         false,
         imTimeoutMs,
-        eventMin);
+        eventMin,
+        ChipICDClient.isPeerICDClient(
+            ChipInteractionClient.getFabricIndex(devicePtr),
+            ChipInteractionClient.getRemoteDeviceId(devicePtr)));
   }
 
   /**
@@ -936,7 +945,10 @@
         keepSubscriptions,
         isFabricFiltered,
         imTimeoutMs,
-        null);
+        null,
+        ChipICDClient.isPeerICDClient(
+            ChipInteractionClient.getFabricIndex(devicePtr),
+            ChipInteractionClient.getRemoteDeviceId(devicePtr)));
   }
 
   /**
@@ -988,7 +1000,10 @@
         keepSubscriptions,
         isFabricFiltered,
         imTimeoutMs,
-        null);
+        null,
+        ChipICDClient.isPeerICDClient(
+            ChipInteractionClient.getFabricIndex(devicePtr),
+            ChipInteractionClient.getRemoteDeviceId(devicePtr)));
   }
 
   public void subscribeToPath(
@@ -1019,7 +1034,47 @@
         keepSubscriptions,
         isFabricFiltered,
         imTimeoutMs,
-        eventMin);
+        eventMin,
+        ChipICDClient.isPeerICDClient(
+            ChipInteractionClient.getFabricIndex(devicePtr),
+            ChipInteractionClient.getRemoteDeviceId(devicePtr)));
+  }
+
+  public void subscribeToPath(
+      SubscriptionEstablishedCallback subscriptionEstablishedCallback,
+      ResubscriptionAttemptCallback resubscriptionAttemptCallback,
+      ReportCallback reportCallback,
+      long devicePtr,
+      List<ChipAttributePath> attributePaths,
+      List<ChipEventPath> eventPaths,
+      int minInterval,
+      int maxInterval,
+      boolean keepSubscriptions,
+      boolean isFabricFiltered,
+      int imTimeoutMs,
+      @Nullable Long eventMin,
+      @Nullable Boolean isPeerLIT) {
+    ReportCallbackJni jniCallback =
+        new ReportCallbackJni(
+            subscriptionEstablishedCallback, reportCallback, resubscriptionAttemptCallback);
+    ChipInteractionClient.subscribe(
+        deviceControllerPtr,
+        jniCallback.getCallbackHandle(),
+        devicePtr,
+        attributePaths,
+        eventPaths,
+        null,
+        minInterval,
+        maxInterval,
+        keepSubscriptions,
+        isFabricFiltered,
+        imTimeoutMs,
+        eventMin,
+        isPeerLIT != null
+            ? isPeerLIT
+            : ChipICDClient.isPeerICDClient(
+                ChipInteractionClient.getFabricIndex(devicePtr),
+                ChipInteractionClient.getRemoteDeviceId(devicePtr)));
   }
 
   /**
@@ -1526,8 +1581,6 @@
   private native void updateCommissioningICDRegistrationInfo(
       long deviceControllerPtr, ICDRegistrationInfo icdRegistrationInfo);
 
-  private native List<ICDClientInfo> getICDClientInfo(long deviceControllerPtr, long fabricIndex);
-
   private native long onNOCChainGeneration(long deviceControllerPtr, ControllerParams params);
 
   private native int getFabricIndex(long deviceControllerPtr);
diff --git a/src/controller/java/src/chip/devicecontroller/ChipICDClient.java b/src/controller/java/src/chip/devicecontroller/ChipICDClient.java
new file mode 100644
index 0000000..4d95a09
--- /dev/null
+++ b/src/controller/java/src/chip/devicecontroller/ChipICDClient.java
@@ -0,0 +1,34 @@
+/*
+ *   Copyright (c) 2024 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.devicecontroller;
+
+import java.util.List;
+
+public class ChipICDClient {
+  public static boolean isPeerICDClient(int fabricIndex, long deviceId) {
+    List<ICDClientInfo> clientInfo = getICDClientInfo(fabricIndex);
+    if (clientInfo == null) {
+      return false;
+    }
+
+    return clientInfo.stream().anyMatch(info -> info.getPeerNodeId() == deviceId);
+  }
+
+  public static native List<ICDClientInfo> getICDClientInfo(int fabricIndex);
+}
diff --git a/src/controller/java/src/chip/devicecontroller/ChipInteractionClient.java b/src/controller/java/src/chip/devicecontroller/ChipInteractionClient.java
index d06f117..a20d1ce 100644
--- a/src/controller/java/src/chip/devicecontroller/ChipInteractionClient.java
+++ b/src/controller/java/src/chip/devicecontroller/ChipInteractionClient.java
@@ -37,7 +37,8 @@
       boolean keepSubscriptions,
       boolean isFabricFiltered,
       int imTimeoutMs,
-      @Nullable Long eventMin);
+      @Nullable Long eventMin,
+      boolean isPeerICD);
 
   static native void read(
       long deviceControllerPtr,
@@ -79,4 +80,8 @@
       @Nullable Integer fabricIndex,
       @Nullable Long peerNodeId,
       @Nullable Long subscriptionId);
+
+  static native long getRemoteDeviceId(long devicePtr);
+
+  static native int getFabricIndex(long devicePtr);
 }
diff --git a/src/controller/java/src/matter/controller/ICDClientInfo.kt b/src/controller/java/src/matter/controller/ICDClientInfo.kt
new file mode 100644
index 0000000..3f0f302
--- /dev/null
+++ b/src/controller/java/src/matter/controller/ICDClientInfo.kt
@@ -0,0 +1,31 @@
+/*
+ *   Copyright (c) 2024 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 matter.controller
+
+/** Class for holding ICD Client information. */
+data class ICDClientInfo(
+  val peerNodeId: Long,
+  val startCounter: Long,
+  val offset: Long,
+  val monitoredSubject: Long,
+  val icdAesKey: ByteArray,
+  val icdHmacKey: ByteArray
+) {
+  override fun toString(): String = "$peerNodeId/$startCounter/$offset/$monitoredSubject"
+}
diff --git a/src/controller/java/src/matter/controller/MatterControllerImpl.kt b/src/controller/java/src/matter/controller/MatterControllerImpl.kt
index dde29ee..1004b64 100644
--- a/src/controller/java/src/matter/controller/MatterControllerImpl.kt
+++ b/src/controller/java/src/matter/controller/MatterControllerImpl.kt
@@ -204,6 +204,9 @@
             reportHandler,
             resubscriptionAttemptHandler
           )
+
+        val fabricIndex = getFabricIndex(devicePtr)
+        val deviceId = getRemoteDeviceId(devicePtr)
         subscribe(
           deviceControllerPtr,
           reportCallbackJni.getJniHandle(),
@@ -214,7 +217,8 @@
           request.maxInterval.seconds.toInt(),
           request.keepSubscriptions,
           request.fabricFiltered,
-          CHIP_IM_TIMEOUT_MS
+          CHIP_IM_TIMEOUT_MS,
+          MatterICDClientImpl.isPeerICDClient(fabricIndex, deviceId)
         )
 
         awaitClose { logger.log(Level.FINE, "Closing flow") }
@@ -232,7 +236,8 @@
     maxInterval: Int,
     keepSubscriptions: Boolean,
     isFabricFiltered: Boolean,
-    imTimeoutMs: Int
+    imTimeoutMs: Int,
+    isPeerLIT: Boolean
   )
 
   override suspend fun read(request: ReadRequest): ReadResponse {
@@ -450,6 +455,10 @@
     imTimeoutMs: Int
   )
 
+  external fun getRemoteDeviceId(devicePtr: Long): Long
+
+  external fun getFabricIndex(devicePtr: Long): Int
+
   override fun close() {
     logger.log(Level.INFO, "MatterController is closed")
     deviceController.shutdownCommissioning()
@@ -477,65 +486,6 @@
     }
   }
 
-  // private fun ChipAttributePath.wrap(): AttributePath {
-  //   return AttributePath(
-  //     endpointId.getId().toUShort(),
-  //     clusterId.getId().toUInt(),
-  //     attributeId.getId().toUInt()
-  //   )
-  // }
-
-  // private fun ChipEventPath.wrap(): EventPath {
-  //   return EventPath(
-  //     endpointId.getId().toUShort(),
-  //     clusterId.getId().toUInt(),
-  //     eventId.getId().toUInt()
-  //   )
-  // }
-
-  // private fun chip.devicecontroller.model.NodeState.wrap(): NodeState {
-  //   return NodeState(
-  //     endpoints = endpointStates.mapValues { (id, value) -> value.wrap(id) }.toMutableMap(),
-  //   )
-  // }
-
-  // private fun chip.devicecontroller.model.EndpointState.wrap(id: Int): EndpointState {
-  //   return EndpointState(
-  //     id,
-  //     clusterStates.mapValues { (id, value) -> value.wrap(id) }.toMutableMap()
-  //   )
-  // }
-
-  // private fun chip.devicecontroller.model.ClusterState.wrap(id: Long): ClusterState {
-  //   return ClusterState(
-  //     id,
-  //     attributeStates.mapValues { (id, value) -> value.wrap(id) }.toMutableMap(),
-  //     eventStates
-  //       .mapValues { (id, value) ->
-  //         value.map { eventState -> eventState.wrap(id) }.toMutableList()
-  //       }
-  //       .toMutableMap()
-  //   )
-  // }
-
-  // private fun chip.devicecontroller.model.AttributeState.wrap(id: Long): AttributeState {
-  //   return AttributeState(id, tlv, json.toString(), AttributePath(0U, 0U, id.toUInt()), value)
-  // }
-
-  // private fun chip.devicecontroller.model.EventState.wrap(id: Long): EventState {
-  //   return EventState(
-  //     id,
-  //     eventNumber,
-  //     priorityLevel,
-  //     timestampType,
-  //     timestampValue,
-  //     tlv,
-  //     EventPath(0U, 0U, id.toUInt()),
-  //     json.toString(),
-  //     value
-  //   )
-  // }
-
   init {
     val config: OperationalKeyConfig? = params.operationalKeyConfig
     val paramsBuilder =
diff --git a/src/controller/java/src/matter/controller/MatterICDClientImpl.kt b/src/controller/java/src/matter/controller/MatterICDClientImpl.kt
new file mode 100644
index 0000000..ae91e34
--- /dev/null
+++ b/src/controller/java/src/matter/controller/MatterICDClientImpl.kt
@@ -0,0 +1,28 @@
+/*
+ *   Copyright (c) 2024 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 matter.controller
+
+object MatterICDClientImpl {
+  fun isPeerICDClient(fabricIndex: Int, deviceId: Long): Boolean {
+    val clientInfo = getICDClientInfo(fabricIndex) ?: return false
+    return clientInfo.firstOrNull { it.peerNodeId == deviceId } != null
+  }
+
+  external fun getICDClientInfo(fabricIndex: Int): List<ICDClientInfo>?
+}