[Fabric-Admin] Refactor to use API methods instead of PushCommand (2/3) (#35867)

* Refactor to use API methods instead of PushCommand for pairing

* Restyled by whitespace

* Restyled by clang-format

* Schedule work on the Matter thread

* Update examples/fabric-admin/device_manager/PairingManager.cpp

Co-authored-by: Terence Hampson <thampson@google.com>

* Update examples/fabric-admin/device_manager/PairingManager.cpp

Co-authored-by: Terence Hampson <thampson@google.com>

* Update examples/fabric-admin/device_manager/PairingManager.cpp

Co-authored-by: Terence Hampson <thampson@google.com>

* Update examples/fabric-admin/device_manager/PairingManager.cpp

Co-authored-by: Terence Hampson <thampson@google.com>

* Address review comments

* Cleanup CCTRL attestation logic from PairingCommand

---------

Co-authored-by: Restyled.io <commits@restyled.io>
Co-authored-by: Terence Hampson <thampson@google.com>
diff --git a/examples/fabric-admin/commands/common/CHIPCommand.cpp b/examples/fabric-admin/commands/common/CHIPCommand.cpp
index b18eb9f..c23ea82 100644
--- a/examples/fabric-admin/commands/common/CHIPCommand.cpp
+++ b/examples/fabric-admin/commands/common/CHIPCommand.cpp
@@ -182,7 +182,7 @@
     mCredIssuerCmds->SetCredentialIssuerOption(CredentialIssuerCommands::CredentialIssuerOptions::kAllowTestCdSigningKey,
                                                allowTestCdSigningKey);
 
-    PairingManager::Instance().Init(&CurrentCommissioner());
+    ReturnLogErrorOnFailure(PairingManager::Instance().Init(&CurrentCommissioner(), mCredIssuerCmds));
 
     return CHIP_NO_ERROR;
 }
diff --git a/examples/fabric-admin/commands/common/CHIPCommand.h b/examples/fabric-admin/commands/common/CHIPCommand.h
index abd68f6..79d1fcb 100644
--- a/examples/fabric-admin/commands/common/CHIPCommand.h
+++ b/examples/fabric-admin/commands/common/CHIPCommand.h
@@ -120,6 +120,8 @@
         StopWaiting();
     }
 
+    static chip::app::DefaultICDClientStorage sICDClientStorage;
+
 protected:
     // Will be called in a setting in which it's safe to touch the CHIP
     // stack. The rules for Run() are as follows:
@@ -167,7 +169,6 @@
     static chip::Crypto::RawKeySessionKeystore sSessionKeystore;
 
     static chip::Credentials::GroupDataProviderImpl sGroupDataProvider;
-    static chip::app::DefaultICDClientStorage sICDClientStorage;
     static chip::app::CheckInHandler sCheckInHandler;
     CredentialIssuerCommands * mCredIssuerCmds;
 
diff --git a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp
index 6e1b198..c8d9f3d 100644
--- a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp
+++ b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp
@@ -94,15 +94,8 @@
         return CHIP_NO_ERROR;
     }
 
-    PairingCommand * pairingCommand = static_cast<PairingCommand *>(CommandMgr().GetCommandByName("pairing", "already-discovered"));
+    PairingManager::Instance().SetCommissioningDelegate(this);
 
-    if (pairingCommand == nullptr)
-    {
-        ChipLogError(NotSpecified, "Pairing already-discovered command is not available");
-        return CHIP_ERROR_NOT_IMPLEMENTED;
-    }
-
-    pairingCommand->RegisterCommissioningDelegate(this);
     mBridgeNodeId = remoteId;
 
     DeviceMgr().PairRemoteFabricBridge(remoteId, mSetupPINCode, reinterpret_cast<const char *>(mRemoteAddr.data()), mRemotePort);
@@ -146,16 +139,7 @@
 
     mBridgeNodeId = bridgeNodeId;
 
-    PairingCommand * pairingCommand = static_cast<PairingCommand *>(CommandMgr().GetCommandByName("pairing", "unpair"));
-
-    if (pairingCommand == nullptr)
-    {
-        ChipLogError(NotSpecified, "Pairing unpair command is not available");
-        return CHIP_ERROR_NOT_IMPLEMENTED;
-    }
-
-    pairingCommand->RegisterPairingDelegate(this);
-
+    PairingManager::Instance().SetPairingDelegate(this);
     DeviceMgr().UnpairRemoteFabricBridge();
 
     return CHIP_NO_ERROR;
@@ -203,10 +187,7 @@
         return CHIP_NO_ERROR;
     }
 
-    PairingCommand * pairingCommand = static_cast<PairingCommand *>(CommandMgr().GetCommandByName("pairing", "already-discovered"));
-    VerifyOrDie(pairingCommand != nullptr);
-
-    pairingCommand->RegisterCommissioningDelegate(this);
+    PairingManager::Instance().SetCommissioningDelegate(this);
     mLocalBridgeNodeId = deviceId;
 
     if (mSetupPINCode.HasValue())
@@ -259,16 +240,7 @@
 
     mLocalBridgeNodeId = bridgeNodeId;
 
-    PairingCommand * pairingCommand = static_cast<PairingCommand *>(CommandMgr().GetCommandByName("pairing", "unpair"));
-
-    if (pairingCommand == nullptr)
-    {
-        ChipLogError(NotSpecified, "Pairing unpair command is not available");
-        return CHIP_ERROR_NOT_IMPLEMENTED;
-    }
-
-    pairingCommand->RegisterPairingDelegate(this);
-
+    PairingManager::Instance().SetPairingDelegate(this);
     DeviceMgr().UnpairLocalFabricBridge();
 
     return CHIP_NO_ERROR;
@@ -287,15 +259,7 @@
         {
             NodeId nodeId = DeviceMgr().GetNextAvailableNodeId();
 
-            PairingCommand * pairingCommand = static_cast<PairingCommand *>(CommandMgr().GetCommandByName("pairing", "code"));
-
-            if (pairingCommand == nullptr)
-            {
-                ChipLogError(NotSpecified, "Pairing code command is not available");
-                return;
-            }
-
-            pairingCommand->RegisterCommissioningDelegate(this);
+            PairingManager::Instance().SetCommissioningDelegate(this);
             mAssignedNodeId = nodeId;
 
             usleep(kCommissionPrepareTimeMs * 1000);
diff --git a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h
index e80f101..16ee40e 100644
--- a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h
+++ b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h
@@ -19,12 +19,10 @@
 #pragma once
 
 #include <commands/common/CHIPCommand.h>
-#include <commands/pairing/PairingCommand.h>
 #include <device_manager/PairingManager.h>
 
 // Constants
 constexpr uint32_t kCommissionPrepareTimeMs = 500;
-constexpr uint16_t kMaxManualCodeLength     = 21;
 
 class FabricSyncAddBridgeCommand : public CHIPCommand, public CommissioningDelegate
 {
diff --git a/examples/fabric-admin/commands/pairing/PairingCommand.cpp b/examples/fabric-admin/commands/pairing/PairingCommand.cpp
index 12a6a28..3757847 100644
--- a/examples/fabric-admin/commands/pairing/PairingCommand.cpp
+++ b/examples/fabric-admin/commands/pairing/PairingCommand.cpp
@@ -30,7 +30,6 @@
 #include <protocols/secure_channel/PASESession.h>
 #include <setup_payload/ManualSetupPayloadParser.h>
 #include <setup_payload/QRCodeSetupPayloadParser.h>
-#include <setup_payload/SetupPayload.h>
 
 #include <string>
 
@@ -41,28 +40,6 @@
 using namespace ::chip;
 using namespace ::chip::Controller;
 
-namespace {
-
-CHIP_ERROR GetPayload(const char * setUpCode, SetupPayload & payload)
-{
-    VerifyOrReturnValue(setUpCode, CHIP_ERROR_INVALID_ARGUMENT);
-    bool isQRCode = strncmp(setUpCode, kQRCodePrefix, strlen(kQRCodePrefix)) == 0;
-    if (isQRCode)
-    {
-        ReturnErrorOnFailure(QRCodeSetupPayloadParser(setUpCode).populatePayload(payload));
-        VerifyOrReturnError(payload.isValidQRCodePayload(), CHIP_ERROR_INVALID_ARGUMENT);
-    }
-    else
-    {
-        ReturnErrorOnFailure(ManualSetupPayloadParser(setUpCode).populatePayload(payload));
-        VerifyOrReturnError(payload.isValidManualCode(), CHIP_ERROR_INVALID_ARGUMENT);
-    }
-
-    return CHIP_NO_ERROR;
-}
-
-} // namespace
-
 CHIP_ERROR PairingCommand::RunCommand()
 {
     CurrentCommissioner().RegisterPairingDelegate(this);
@@ -128,7 +105,10 @@
 {
     auto params = CommissioningParameters();
     params.SetSkipCommissioningComplete(mSkipCommissioningComplete.ValueOr(false));
-    params.SetDeviceAttestationDelegate(this);
+    if (mBypassAttestationVerifier.ValueOr(false))
+    {
+        params.SetDeviceAttestationDelegate(this);
+    }
 
     switch (mNetworkType)
     {
@@ -442,12 +422,6 @@
         ChipLogProgress(NotSpecified, "Device commissioning Failure: %s", ErrorStr(err));
     }
 
-    if (mCommissioningDelegate)
-    {
-        mCommissioningDelegate->OnCommissioningComplete(nodeId, err);
-        this->UnregisterCommissioningDelegate();
-    }
-
     SetCommandExitStatus(err);
 }
 
@@ -576,13 +550,6 @@
         ChipLogProgress(NotSpecified, "Device unpair Failure: " ChipLogFormatX64 " %s", ChipLogValueX64(nodeId), ErrorStr(err));
     }
 
-    PairingDelegate * pairingDelegate = command->GetPairingDelegate();
-    if (pairingDelegate)
-    {
-        pairingDelegate->OnDeviceRemoved(nodeId, err);
-        command->UnregisterPairingDelegate();
-    }
-
     command->SetCommandExitStatus(err);
 }
 
@@ -592,77 +559,13 @@
     return Optional<uint16_t>();
 }
 
-bool PairingCommand::ShouldWaitAfterDeviceAttestation()
-{
-    // If there is a vendor ID and product ID, request OnDeviceAttestationCompleted().
-    // Currently this is added in the case that the example is performing reverse commissioning,
-    // but it would be an improvement to store that explicitly.
-    // TODO: Issue #35297 - [Fabric Sync] Improve where we get VID and PID when validating CCTRL CommissionNode command
-    SetupPayload payload;
-    CHIP_ERROR err = GetPayload(mOnboardingPayload, payload);
-    return err == CHIP_NO_ERROR && (payload.vendorID != 0 || payload.productID != 0);
-}
-
 void PairingCommand::OnDeviceAttestationCompleted(Controller::DeviceCommissioner * deviceCommissioner, DeviceProxy * device,
                                                   const Credentials::DeviceAttestationVerifier::AttestationDeviceInfo & info,
                                                   Credentials::AttestationVerificationResult attestationResult)
 {
-    SetupPayload payload;
-    CHIP_ERROR parse_error = GetPayload(mOnboardingPayload, payload);
-    if (parse_error == CHIP_NO_ERROR && (payload.vendorID != 0 || payload.productID != 0))
-    {
-        if (payload.vendorID == 0 || payload.productID == 0)
-        {
-            ChipLogProgress(NotSpecified,
-                            "Failed validation: vendorID or productID must not be 0."
-                            "Requested VID: %u, Requested PID: %u.",
-                            payload.vendorID, payload.productID);
-            deviceCommissioner->ContinueCommissioningAfterDeviceAttestation(
-                device, Credentials::AttestationVerificationResult::kInvalidArgument);
-            return;
-        }
-
-        if (payload.vendorID != info.BasicInformationVendorId() || payload.productID != info.BasicInformationProductId())
-        {
-            ChipLogProgress(NotSpecified,
-                            "Failed validation of vendorID or productID."
-                            "Requested VID: %u, Requested PID: %u,"
-                            "Detected VID: %u, Detected PID %u.",
-                            payload.vendorID, payload.productID, info.BasicInformationVendorId(), info.BasicInformationProductId());
-            deviceCommissioner->ContinueCommissioningAfterDeviceAttestation(
-                device,
-                payload.vendorID == info.BasicInformationVendorId()
-                    ? Credentials::AttestationVerificationResult::kDacProductIdMismatch
-                    : Credentials::AttestationVerificationResult::kDacVendorIdMismatch);
-            return;
-        }
-
-        // NOTE: This will log errors even if the attestion was successful.
-        auto err = deviceCommissioner->ContinueCommissioningAfterDeviceAttestation(device, attestationResult);
-        if (CHIP_NO_ERROR != err)
-        {
-            SetCommandExitStatus(err);
-        }
-        return;
-    }
-
-    // OnDeviceAttestationCompleted() is called if ShouldWaitAfterDeviceAttestation() returns true
-    // or if there is an attestation error. The conditions for ShouldWaitAfterDeviceAttestation() have
-    // already been checked, so the call to OnDeviceAttestationCompleted() was an error.
-    if (mBypassAttestationVerifier.ValueOr(false))
-    {
-        // Bypass attestation verification, continue with success
-        auto err = deviceCommissioner->ContinueCommissioningAfterDeviceAttestation(
-            device, Credentials::AttestationVerificationResult::kSuccess);
-        if (CHIP_NO_ERROR != err)
-        {
-            SetCommandExitStatus(err);
-        }
-        return;
-    }
-
-    // Don't bypass attestation, continue with error.
-    auto err = deviceCommissioner->ContinueCommissioningAfterDeviceAttestation(device, attestationResult);
+    // Bypass attestation verification, continue with success
+    auto err = deviceCommissioner->ContinueCommissioningAfterDeviceAttestation(
+        device, Credentials::AttestationVerificationResult::kSuccess);
     if (CHIP_NO_ERROR != err)
     {
         SetCommandExitStatus(err);
diff --git a/examples/fabric-admin/commands/pairing/PairingCommand.h b/examples/fabric-admin/commands/pairing/PairingCommand.h
index 73e717b..aafd888 100644
--- a/examples/fabric-admin/commands/pairing/PairingCommand.h
+++ b/examples/fabric-admin/commands/pairing/PairingCommand.h
@@ -45,20 +45,6 @@
     Thread,
 };
 
-class CommissioningDelegate
-{
-public:
-    virtual void OnCommissioningComplete(chip::NodeId deviceId, CHIP_ERROR err) = 0;
-    virtual ~CommissioningDelegate()                                            = default;
-};
-
-class PairingDelegate
-{
-public:
-    virtual void OnDeviceRemoved(chip::NodeId deviceId, CHIP_ERROR err) = 0;
-    virtual ~PairingDelegate()                                          = default;
-};
-
 class PairingCommand : public CHIPCommand,
                        public chip::Controller::DevicePairingDelegate,
                        public chip::Controller::DeviceDiscoveryDelegate,
@@ -221,20 +207,10 @@
 
     /////////// DeviceAttestationDelegate Interface /////////
     chip::Optional<uint16_t> FailSafeExpiryTimeoutSecs() const override;
-    bool ShouldWaitAfterDeviceAttestation() override;
     void OnDeviceAttestationCompleted(chip::Controller::DeviceCommissioner * deviceCommissioner, chip::DeviceProxy * device,
                                       const chip::Credentials::DeviceAttestationVerifier::AttestationDeviceInfo & info,
                                       chip::Credentials::AttestationVerificationResult attestationResult) override;
 
-    /////////// CommissioningDelegate /////////
-    void RegisterCommissioningDelegate(CommissioningDelegate * delegate) { mCommissioningDelegate = delegate; }
-    void UnregisterCommissioningDelegate() { mCommissioningDelegate = nullptr; }
-
-    /////////// PairingDelegate /////////
-    void RegisterPairingDelegate(PairingDelegate * delegate) { mPairingDelegate = delegate; }
-    void UnregisterPairingDelegate() { mPairingDelegate = nullptr; }
-    PairingDelegate * GetPairingDelegate() { return mPairingDelegate; }
-
 private:
     CHIP_ERROR RunInternal(NodeId remoteId);
     CHIP_ERROR Pair(NodeId remoteId, PeerAddress address);
@@ -290,9 +266,6 @@
     chip::Platform::UniquePtr<chip::Controller::CurrentFabricRemover> mCurrentFabricRemover;
     chip::Callback::Callback<chip::Controller::OnCurrentFabricRemove> mCurrentFabricRemoveCallback;
 
-    CommissioningDelegate * mCommissioningDelegate = nullptr;
-    PairingDelegate * mPairingDelegate             = nullptr;
-
     static void OnCurrentFabricRemove(void * context, NodeId remoteNodeId, CHIP_ERROR status);
     void PersistIcdInfo();
 };
diff --git a/examples/fabric-admin/device_manager/DeviceManager.cpp b/examples/fabric-admin/device_manager/DeviceManager.cpp
index f34a844..4f7b1c7 100644
--- a/examples/fabric-admin/device_manager/DeviceManager.cpp
+++ b/examples/fabric-admin/device_manager/DeviceManager.cpp
@@ -20,7 +20,6 @@
 
 #include <commands/interactive/InteractiveCommands.h>
 #include <crypto/RandUtils.h>
-#include <device_manager/PairingManager.h>
 #include <lib/support/StringBuilder.h>
 
 #include <cstdio>
@@ -153,52 +152,42 @@
 void DeviceManager::PairRemoteFabricBridge(NodeId nodeId, uint32_t setupPINCode, const char * deviceRemoteIp,
                                            uint16_t deviceRemotePort)
 {
-    StringBuilder<kMaxCommandSize> commandBuilder;
-
-    commandBuilder.Add("pairing already-discovered ");
-    commandBuilder.AddFormat("%lu %d %s %d", nodeId, setupPINCode, deviceRemoteIp, deviceRemotePort);
-
-    PushCommand(commandBuilder.c_str());
+    if (PairingManager::Instance().PairDevice(nodeId, setupPINCode, deviceRemoteIp, deviceRemotePort) != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to pair remote fabric bridge " ChipLogFormatX64, ChipLogValueX64(nodeId));
+    }
 }
 
 void DeviceManager::PairRemoteDevice(NodeId nodeId, const char * payload)
 {
-    StringBuilder<kMaxCommandSize> commandBuilder;
-
-    commandBuilder.Add("pairing code ");
-    commandBuilder.AddFormat("%lu %s", nodeId, payload);
-
-    PushCommand(commandBuilder.c_str());
+    if (PairingManager::Instance().PairDeviceWithCode(nodeId, payload) != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to pair remote device " ChipLogFormatX64, ChipLogValueX64(nodeId));
+    }
 }
 
 void DeviceManager::PairLocalFabricBridge(NodeId nodeId)
 {
-    StringBuilder<kMaxCommandSize> commandBuilder;
-
-    commandBuilder.Add("pairing already-discovered ");
-    commandBuilder.AddFormat("%lu %d ::1 %d", nodeId, mLocalBridgeSetupPinCode, mLocalBridgePort);
-
-    PushCommand(commandBuilder.c_str());
+    if (PairingManager::Instance().PairDevice(nodeId, mLocalBridgeSetupPinCode, "::1", mLocalBridgePort) != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to pair local fabric bridge " ChipLogFormatX64, ChipLogValueX64(nodeId));
+    }
 }
 
 void DeviceManager::UnpairRemoteFabricBridge()
 {
-    StringBuilder<kMaxCommandSize> commandBuilder;
-
-    commandBuilder.Add("pairing unpair ");
-    commandBuilder.AddFormat("%lu", mRemoteBridgeNodeId);
-
-    PushCommand(commandBuilder.c_str());
+    if (PairingManager::Instance().UnpairDevice(mRemoteBridgeNodeId) != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to unpair remote bridge device " ChipLogFormatX64, ChipLogValueX64(mRemoteBridgeNodeId));
+    }
 }
 
 void DeviceManager::UnpairLocalFabricBridge()
 {
-    StringBuilder<kMaxCommandSize> commandBuilder;
-
-    commandBuilder.Add("pairing unpair ");
-    commandBuilder.AddFormat("%lu", mLocalBridgeNodeId);
-
-    PushCommand(commandBuilder.c_str());
+    if (PairingManager::Instance().UnpairDevice(mLocalBridgeNodeId) != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to unpair local bridge device " ChipLogFormatX64, ChipLogValueX64(mLocalBridgeNodeId));
+    }
 }
 
 void DeviceManager::SubscribeRemoteFabricBridge()
@@ -390,20 +379,15 @@
 
         if (mAutoSyncEnabled)
         {
-            StringBuilder<kMaxCommandSize> commandBuilder;
-            commandBuilder.Add("pairing unpair ");
-            commandBuilder.AddFormat("%lu", device->GetNodeId());
-
-            PairingCommand * pairingCommand = static_cast<PairingCommand *>(CommandMgr().GetCommandByName("pairing", "unpair"));
-
-            if (pairingCommand == nullptr)
+            NodeId nodeId = device->GetNodeId();
+            if (PairingManager::Instance().UnpairDevice(nodeId) != CHIP_NO_ERROR)
             {
-                ChipLogError(NotSpecified, "Pairing code command is not available");
-                return;
+                ChipLogError(NotSpecified, "Failed to unpair device " ChipLogFormatX64, ChipLogValueX64(nodeId));
             }
-
-            pairingCommand->RegisterPairingDelegate(this);
-            PushCommand(commandBuilder.c_str());
+            else
+            {
+                PairingManager::Instance().SetPairingDelegate(this);
+            }
         }
     }
 }
diff --git a/examples/fabric-admin/device_manager/DeviceManager.h b/examples/fabric-admin/device_manager/DeviceManager.h
index 939329b..5e43e78 100644
--- a/examples/fabric-admin/device_manager/DeviceManager.h
+++ b/examples/fabric-admin/device_manager/DeviceManager.h
@@ -19,7 +19,7 @@
 #pragma once
 
 #include <app-common/zap-generated/cluster-objects.h>
-#include <commands/pairing/PairingCommand.h>
+#include <device_manager/PairingManager.h>
 #include <platform/CHIPDeviceLayer.h>
 
 #include <set>
diff --git a/examples/fabric-admin/device_manager/PairingManager.cpp b/examples/fabric-admin/device_manager/PairingManager.cpp
index aa56af4..c46a0ca 100644
--- a/examples/fabric-admin/device_manager/PairingManager.cpp
+++ b/examples/fabric-admin/device_manager/PairingManager.cpp
@@ -18,21 +18,100 @@
 
 #include "PairingManager.h"
 
-#include <controller/CommissioningDelegate.h>
-#include <controller/CurrentFabricRemover.h>
-#include <lib/support/Span.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <commands/common/CHIPCommand.h>
+#include <device_manager/DeviceSynchronization.h>
+#include <lib/support/logging/CHIPLogging.h>
+#include <setup_payload/ManualSetupPayloadParser.h>
+#include <setup_payload/QRCodeSetupPayloadParser.h>
+
+#if defined(PW_RPC_ENABLED)
+#include <rpc/RpcClient.h>
+#endif
 
 using namespace ::chip;
+using namespace ::chip::Controller;
+
+namespace {
+
+CHIP_ERROR GetPayload(const char * setUpCode, SetupPayload & payload)
+{
+    VerifyOrReturnValue(setUpCode, CHIP_ERROR_INVALID_ARGUMENT);
+    bool isQRCode = strncmp(setUpCode, kQRCodePrefix, strlen(kQRCodePrefix)) == 0;
+    if (isQRCode)
+    {
+        ReturnErrorOnFailure(QRCodeSetupPayloadParser(setUpCode).populatePayload(payload));
+        VerifyOrReturnError(payload.isValidQRCodePayload(), CHIP_ERROR_INVALID_ARGUMENT);
+    }
+    else
+    {
+        ReturnErrorOnFailure(ManualSetupPayloadParser(setUpCode).populatePayload(payload));
+        VerifyOrReturnError(payload.isValidManualCode(), CHIP_ERROR_INVALID_ARGUMENT);
+    }
+
+    return CHIP_NO_ERROR;
+}
+
+bool ParseAddressWithInterface(const char * addressString, Inet::IPAddress & address, Inet::InterfaceId & interfaceId)
+{
+    struct addrinfo hints;
+    struct addrinfo * result;
+    int ret;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family   = AF_UNSPEC;
+    hints.ai_socktype = SOCK_DGRAM;
+    ret               = getaddrinfo(addressString, nullptr, &hints, &result);
+    if (ret < 0)
+    {
+        ChipLogError(NotSpecified, "Invalid address: %s", addressString);
+        return false;
+    }
+
+    if (result->ai_family == AF_INET6)
+    {
+        struct sockaddr_in6 * addr = reinterpret_cast<struct sockaddr_in6 *>(result->ai_addr);
+        address                    = Inet::IPAddress::FromSockAddr(*addr);
+        interfaceId                = Inet::InterfaceId(addr->sin6_scope_id);
+    }
+#if INET_CONFIG_ENABLE_IPV4
+    else if (result->ai_family == AF_INET)
+    {
+        address     = Inet::IPAddress::FromSockAddr(*reinterpret_cast<struct sockaddr_in *>(result->ai_addr));
+        interfaceId = Inet::InterfaceId::Null();
+    }
+#endif // INET_CONFIG_ENABLE_IPV4
+    else
+    {
+        ChipLogError(NotSpecified, "Unsupported address: %s", addressString);
+        freeaddrinfo(result);
+        return false;
+    }
+
+    freeaddrinfo(result);
+    return true;
+}
+
+} // namespace
 
 PairingManager::PairingManager() :
     mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this),
-    mOnOpenCommissioningWindowVerifierCallback(OnOpenCommissioningWindowVerifierResponse, this)
+    mOnOpenCommissioningWindowVerifierCallback(OnOpenCommissioningWindowVerifierResponse, this),
+    mCurrentFabricRemoveCallback(OnCurrentFabricRemove, this)
 {}
 
-void PairingManager::Init(Controller::DeviceCommissioner * commissioner)
+CHIP_ERROR PairingManager::Init(Controller::DeviceCommissioner * commissioner, CredentialIssuerCommands * credIssuerCmds)
 {
-    VerifyOrDie(mCommissioner == nullptr);
-    mCommissioner = commissioner;
+    VerifyOrReturnError(commissioner != nullptr, CHIP_ERROR_INCORRECT_STATE);
+    VerifyOrReturnError(credIssuerCmds != nullptr, CHIP_ERROR_INCORRECT_STATE);
+
+    mCommissioner   = commissioner;
+    mCredIssuerCmds = credIssuerCmds;
+
+    return CHIP_NO_ERROR;
 }
 
 CHIP_ERROR PairingManager::OpenCommissioningWindow(NodeId nodeId, EndpointId endpointId, uint16_t commissioningTimeoutSec,
@@ -168,3 +247,444 @@
     // Reset the window opener once the window operation is complete
     self->mWindowOpener.reset();
 }
+
+void PairingManager::OnStatusUpdate(DevicePairingDelegate::Status status)
+{
+    switch (status)
+    {
+    case DevicePairingDelegate::Status::SecurePairingSuccess:
+        ChipLogProgress(NotSpecified, "CASE establishment successful");
+        break;
+    case DevicePairingDelegate::Status::SecurePairingFailed:
+        ChipLogError(NotSpecified, "Secure Pairing Failed");
+        break;
+    }
+}
+
+void PairingManager::OnPairingComplete(CHIP_ERROR err)
+{
+    if (err == CHIP_NO_ERROR)
+    {
+        ChipLogProgress(NotSpecified, "PASE establishment successful");
+    }
+    else
+    {
+        ChipLogProgress(NotSpecified, "Pairing Failure: %s", ErrorStr(err));
+    }
+}
+
+void PairingManager::OnPairingDeleted(CHIP_ERROR err)
+{
+    if (err == CHIP_NO_ERROR)
+    {
+        ChipLogProgress(NotSpecified, "Pairing Deleted Success");
+    }
+    else
+    {
+        ChipLogProgress(NotSpecified, "Pairing Deleted Failure: %s", ErrorStr(err));
+    }
+}
+
+void PairingManager::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err)
+{
+    if (err == CHIP_NO_ERROR)
+    {
+        // print to console
+        fprintf(stderr, "New device with Node ID: " ChipLogFormatX64 "has been successfully added.\n", ChipLogValueX64(nodeId));
+
+        // mCommissioner has a lifetime that is the entire life of the application itself
+        // so it is safe to provide to StartDeviceSynchronization.
+        DeviceSynchronizer::Instance().StartDeviceSynchronization(mCommissioner, nodeId, mDeviceIsICD);
+    }
+    else
+    {
+        // When ICD device commissioning fails, the ICDClientInfo stored in OnICDRegistrationComplete needs to be removed.
+        if (mDeviceIsICD)
+        {
+            CHIP_ERROR deleteEntryError =
+                CHIPCommand::sICDClientStorage.DeleteEntry(ScopedNodeId(nodeId, mCommissioner->GetFabricIndex()));
+            if (deleteEntryError != CHIP_NO_ERROR)
+            {
+                ChipLogError(NotSpecified, "Failed to delete ICD entry: %s", ErrorStr(err));
+            }
+        }
+        ChipLogProgress(NotSpecified, "Device commissioning Failure: %s", ErrorStr(err));
+    }
+
+    if (mCommissioningDelegate)
+    {
+        mCommissioningDelegate->OnCommissioningComplete(nodeId, err);
+        SetCommissioningDelegate(nullptr);
+    }
+}
+
+void PairingManager::OnReadCommissioningInfo(const Controller::ReadCommissioningInfo & info)
+{
+    ChipLogProgress(AppServer, "OnReadCommissioningInfo - vendorId=0x%04X productId=0x%04X", info.basic.vendorId,
+                    info.basic.productId);
+
+    // The string in CharSpan received from the device is not null-terminated, we use std::string here for coping and
+    // appending a null-terminator at the end of the string.
+    std::string userActiveModeTriggerInstruction;
+
+    // Note: the callback doesn't own the buffer, should make a copy if it will be used it later.
+    if (info.icd.userActiveModeTriggerInstruction.size() != 0)
+    {
+        userActiveModeTriggerInstruction =
+            std::string(info.icd.userActiveModeTriggerInstruction.data(), info.icd.userActiveModeTriggerInstruction.size());
+    }
+
+    if (info.icd.userActiveModeTriggerHint.HasAny())
+    {
+        ChipLogProgress(AppServer, "OnReadCommissioningInfo - LIT UserActiveModeTriggerHint=0x%08x",
+                        info.icd.userActiveModeTriggerHint.Raw());
+        ChipLogProgress(AppServer, "OnReadCommissioningInfo - LIT UserActiveModeTriggerInstruction=%s",
+                        userActiveModeTriggerInstruction.c_str());
+    }
+    ChipLogProgress(AppServer, "OnReadCommissioningInfo ICD - IdleModeDuration=%u activeModeDuration=%u activeModeThreshold=%u",
+                    info.icd.idleModeDuration, info.icd.activeModeDuration, info.icd.activeModeThreshold);
+}
+
+void PairingManager::OnICDRegistrationComplete(ScopedNodeId nodeId, uint32_t icdCounter)
+{
+    char icdSymmetricKeyHex[Crypto::kAES_CCM128_Key_Length * 2 + 1];
+
+    Encoding::BytesToHex(mICDSymmetricKey.Value().data(), mICDSymmetricKey.Value().size(), icdSymmetricKeyHex,
+                         sizeof(icdSymmetricKeyHex), Encoding::HexFlags::kNullTerminate);
+
+    app::ICDClientInfo clientInfo;
+    clientInfo.peer_node         = nodeId;
+    clientInfo.monitored_subject = mICDMonitoredSubject.Value();
+    clientInfo.start_icd_counter = icdCounter;
+
+    CHIP_ERROR err = CHIPCommand::sICDClientStorage.SetKey(clientInfo, mICDSymmetricKey.Value());
+    if (err == CHIP_NO_ERROR)
+    {
+        err = CHIPCommand::sICDClientStorage.StoreEntry(clientInfo);
+    }
+
+    if (err != CHIP_NO_ERROR)
+    {
+        CHIPCommand::sICDClientStorage.RemoveKey(clientInfo);
+        ChipLogError(NotSpecified, "Failed to persist symmetric key for " ChipLogFormatX64 ": %s",
+                     ChipLogValueX64(nodeId.GetNodeId()), err.AsString());
+        return;
+    }
+
+    mDeviceIsICD = true;
+
+    ChipLogProgress(NotSpecified, "Saved ICD Symmetric key for " ChipLogFormatX64, ChipLogValueX64(nodeId.GetNodeId()));
+    ChipLogProgress(NotSpecified,
+                    "ICD Registration Complete for device " ChipLogFormatX64 " / Check-In NodeID: " ChipLogFormatX64
+                    " / Monitored Subject: " ChipLogFormatX64 " / Symmetric Key: %s / ICDCounter %u",
+                    ChipLogValueX64(nodeId.GetNodeId()), ChipLogValueX64(mICDCheckInNodeId.Value()),
+                    ChipLogValueX64(mICDMonitoredSubject.Value()), icdSymmetricKeyHex, icdCounter);
+}
+
+void PairingManager::OnICDStayActiveComplete(ScopedNodeId deviceId, uint32_t promisedActiveDuration)
+{
+    ChipLogProgress(NotSpecified, "ICD Stay Active Complete for device " ChipLogFormatX64 " / promisedActiveDuration: %u",
+                    ChipLogValueX64(deviceId.GetNodeId()), promisedActiveDuration);
+}
+
+void PairingManager::OnDiscoveredDevice(const Dnssd::CommissionNodeData & nodeData)
+{
+    // Ignore nodes with closed commissioning window
+    VerifyOrReturn(nodeData.commissioningMode != 0);
+
+    auto & resolutionData = nodeData;
+
+    const uint16_t port = resolutionData.port;
+    char buf[Inet::IPAddress::kMaxStringLength];
+    resolutionData.ipAddress[0].ToString(buf);
+    ChipLogProgress(NotSpecified, "Discovered Device: %s:%u", buf, port);
+
+    // Stop Mdns discovery.
+    auto err = mCommissioner->StopCommissionableDiscovery();
+
+    // Some platforms does not implement a mechanism to stop mdns browse, so
+    // we just ignore CHIP_ERROR_NOT_IMPLEMENTED instead of bailing out.
+    if (CHIP_NO_ERROR != err && CHIP_ERROR_NOT_IMPLEMENTED != err)
+    {
+        return;
+    }
+
+    mCommissioner->RegisterDeviceDiscoveryDelegate(nullptr);
+
+    auto interfaceId = resolutionData.ipAddress[0].IsIPv6LinkLocal() ? resolutionData.interfaceId : Inet::InterfaceId::Null();
+    auto peerAddress = Transport::PeerAddress::UDP(resolutionData.ipAddress[0], port, interfaceId);
+    err              = Pair(mNodeId, peerAddress);
+    if (CHIP_NO_ERROR != err)
+    {
+        ChipLogProgress(NotSpecified, "Failed to pair device: " ChipLogFormatX64 " %s", ChipLogValueX64(mNodeId), ErrorStr(err));
+    }
+}
+
+Optional<uint16_t> PairingManager::FailSafeExpiryTimeoutSecs() const
+{
+    // No manual input, so do not need to extend.
+    return Optional<uint16_t>();
+}
+
+bool PairingManager::ShouldWaitAfterDeviceAttestation()
+{
+    // If there is a vendor ID and product ID, request OnDeviceAttestationCompleted().
+    // Currently this is added in the case that the example is performing reverse commissioning,
+    // but it would be an improvement to store that explicitly.
+    // TODO: Issue #35297 - [Fabric Sync] Improve where we get VID and PID when validating CCTRL CommissionNode command
+    SetupPayload payload;
+    CHIP_ERROR err = GetPayload(mOnboardingPayload, payload);
+    return err == CHIP_NO_ERROR && (payload.vendorID != 0 || payload.productID != 0);
+}
+
+void PairingManager::OnDeviceAttestationCompleted(Controller::DeviceCommissioner * deviceCommissioner, DeviceProxy * device,
+                                                  const Credentials::DeviceAttestationVerifier::AttestationDeviceInfo & info,
+                                                  Credentials::AttestationVerificationResult attestationResult)
+{
+    SetupPayload payload;
+    CHIP_ERROR parse_error = GetPayload(mOnboardingPayload, payload);
+    if (parse_error == CHIP_NO_ERROR && (payload.vendorID != 0 || payload.productID != 0))
+    {
+        if (payload.vendorID == 0 || payload.productID == 0)
+        {
+            ChipLogProgress(NotSpecified,
+                            "Failed validation: vendorID or productID must not be 0."
+                            "Requested VID: %u, Requested PID: %u.",
+                            payload.vendorID, payload.productID);
+            deviceCommissioner->ContinueCommissioningAfterDeviceAttestation(
+                device, Credentials::AttestationVerificationResult::kInvalidArgument);
+            return;
+        }
+
+        if (payload.vendorID != info.BasicInformationVendorId() || payload.productID != info.BasicInformationProductId())
+        {
+            ChipLogProgress(NotSpecified,
+                            "Failed validation of vendorID or productID."
+                            "Requested VID: %u, Requested PID: %u,"
+                            "Detected VID: %u, Detected PID %u.",
+                            payload.vendorID, payload.productID, info.BasicInformationVendorId(), info.BasicInformationProductId());
+            deviceCommissioner->ContinueCommissioningAfterDeviceAttestation(
+                device,
+                payload.vendorID == info.BasicInformationVendorId()
+                    ? Credentials::AttestationVerificationResult::kDacProductIdMismatch
+                    : Credentials::AttestationVerificationResult::kDacVendorIdMismatch);
+            return;
+        }
+
+        // NOTE: This will log errors even if the attestion was successful.
+        CHIP_ERROR err = deviceCommissioner->ContinueCommissioningAfterDeviceAttestation(device, attestationResult);
+        if (CHIP_NO_ERROR != err)
+        {
+            ChipLogError(NotSpecified, "Failed to continue commissioning after device attestation, error: %s", ErrorStr(err));
+        }
+        return;
+    }
+
+    // Don't bypass attestation, continue with error.
+    CHIP_ERROR err = deviceCommissioner->ContinueCommissioningAfterDeviceAttestation(device, attestationResult);
+    if (CHIP_NO_ERROR != err)
+    {
+        ChipLogError(NotSpecified, "Failed to continue commissioning after device attestation, error: %s", ErrorStr(err));
+    }
+}
+
+CommissioningParameters PairingManager::GetCommissioningParameters()
+{
+    auto params = CommissioningParameters();
+    params.SetSkipCommissioningComplete(false);
+    params.SetDeviceAttestationDelegate(this);
+
+    if (mICDRegistration.ValueOr(false))
+    {
+        params.SetICDRegistrationStrategy(ICDRegistrationStrategy::kBeforeComplete);
+
+        if (!mICDSymmetricKey.HasValue())
+        {
+            Crypto::DRBG_get_bytes(mRandomGeneratedICDSymmetricKey, sizeof(mRandomGeneratedICDSymmetricKey));
+            mICDSymmetricKey.SetValue(ByteSpan(mRandomGeneratedICDSymmetricKey));
+        }
+        if (!mICDCheckInNodeId.HasValue())
+        {
+            mICDCheckInNodeId.SetValue(mCommissioner->GetNodeId());
+        }
+        if (!mICDMonitoredSubject.HasValue())
+        {
+            mICDMonitoredSubject.SetValue(mICDCheckInNodeId.Value());
+        }
+        if (!mICDClientType.HasValue())
+        {
+            mICDClientType.SetValue(app::Clusters::IcdManagement::ClientTypeEnum::kPermanent);
+        }
+        // These Optionals must have values now.
+        // The commissioner will verify these values.
+        params.SetICDSymmetricKey(mICDSymmetricKey.Value());
+        if (mICDStayActiveDurationMsec.HasValue())
+        {
+            params.SetICDStayActiveDurationMsec(mICDStayActiveDurationMsec.Value());
+        }
+        params.SetICDCheckInNodeId(mICDCheckInNodeId.Value());
+        params.SetICDMonitoredSubject(mICDMonitoredSubject.Value());
+        params.SetICDClientType(mICDClientType.Value());
+    }
+
+    return params;
+}
+
+CHIP_ERROR PairingManager::Pair(NodeId remoteId, Transport::PeerAddress address)
+{
+    auto params = RendezvousParameters().SetSetupPINCode(mSetupPINCode).SetDiscriminator(mDiscriminator).SetPeerAddress(address);
+
+    CHIP_ERROR err           = CHIP_NO_ERROR;
+    auto commissioningParams = GetCommissioningParameters();
+    err                      = CurrentCommissioner().PairDevice(remoteId, params, commissioningParams);
+
+    return err;
+}
+
+void PairingManager::OnCurrentFabricRemove(void * context, NodeId nodeId, CHIP_ERROR err)
+{
+    PairingManager * self = reinterpret_cast<PairingManager *>(context);
+    VerifyOrReturn(self != nullptr, ChipLogError(NotSpecified, "OnCurrentFabricRemove: context is null"));
+
+    if (err == CHIP_NO_ERROR)
+    {
+        // print to console
+        fprintf(stderr, "Device with Node ID: " ChipLogFormatX64 "has been successfully removed.\n", ChipLogValueX64(nodeId));
+
+#if defined(PW_RPC_ENABLED)
+        FabricIndex fabricIndex = self->CurrentCommissioner().GetFabricIndex();
+        app::InteractionModelEngine::GetInstance()->ShutdownSubscriptions(fabricIndex, nodeId);
+        ScopedNodeId scopedNodeId(nodeId, fabricIndex);
+        RemoveSynchronizedDevice(scopedNodeId);
+#endif
+    }
+    else
+    {
+        ChipLogProgress(NotSpecified, "Device unpair Failure: " ChipLogFormatX64 " %s", ChipLogValueX64(nodeId), ErrorStr(err));
+    }
+}
+
+void PairingManager::InitPairingCommand()
+{
+    mCommissioner->RegisterPairingDelegate(this);
+    // Clear the CATs in OperationalCredentialsIssuer
+    mCredIssuerCmds->SetCredentialIssuerCATValues(kUndefinedCATs);
+    mDeviceIsICD = false;
+}
+
+CHIP_ERROR PairingManager::PairDeviceWithCode(NodeId nodeId, const char * payload)
+{
+    if (payload == nullptr || strlen(payload) > kMaxManualCodeLength + 1)
+    {
+        ChipLogError(NotSpecified, "PairDeviceWithCode failed: Invalid pairing payload");
+        return CHIP_ERROR_INVALID_STRING_LENGTH;
+    }
+
+    auto params = Platform::MakeUnique<PairDeviceWithCodeParams>();
+    VerifyOrReturnError(params != nullptr, CHIP_ERROR_NO_MEMORY);
+
+    params->nodeId = nodeId;
+    Platform::CopyString(params->payloadBuffer, sizeof(params->payloadBuffer), payload);
+
+    // Schedule work on the Matter thread
+    return DeviceLayer::PlatformMgr().ScheduleWork(OnPairDeviceWithCode, reinterpret_cast<intptr_t>(params.release()));
+}
+
+void PairingManager::OnPairDeviceWithCode(intptr_t context)
+{
+    Platform::UniquePtr<PairDeviceWithCodeParams> params(reinterpret_cast<PairDeviceWithCodeParams *>(context));
+    PairingManager & self = PairingManager::Instance();
+
+    self.InitPairingCommand();
+
+    CommissioningParameters commissioningParams = self.GetCommissioningParameters();
+    auto discoveryType                          = DiscoveryType::kDiscoveryNetworkOnly;
+
+    self.mNodeId            = params->nodeId;
+    self.mOnboardingPayload = params->payloadBuffer;
+
+    CHIP_ERROR err = self.mCommissioner->PairDevice(params->nodeId, params->payloadBuffer, commissioningParams, discoveryType);
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to pair device with code, error: %s", ErrorStr(err));
+    }
+}
+
+CHIP_ERROR PairingManager::PairDevice(chip::NodeId nodeId, uint32_t setupPINCode, const char * deviceRemoteIp,
+                                      uint16_t deviceRemotePort)
+{
+    if (deviceRemoteIp == nullptr || strlen(deviceRemoteIp) > Inet::IPAddress::kMaxStringLength)
+    {
+        ChipLogError(NotSpecified, "PairDevice failed: Invalid device remote IP address");
+        return CHIP_ERROR_INVALID_STRING_LENGTH;
+    }
+
+    auto params = Platform::MakeUnique<PairDeviceParams>();
+    VerifyOrReturnError(params != nullptr, CHIP_ERROR_NO_MEMORY);
+
+    params->nodeId           = nodeId;
+    params->setupPINCode     = setupPINCode;
+    params->deviceRemotePort = deviceRemotePort;
+
+    Platform::CopyString(params->ipAddrBuffer, sizeof(params->ipAddrBuffer), deviceRemoteIp);
+
+    // Schedule work on the Matter thread
+    return DeviceLayer::PlatformMgr().ScheduleWork(OnPairDevice, reinterpret_cast<intptr_t>(params.release()));
+}
+
+void PairingManager::OnPairDevice(intptr_t context)
+{
+    Platform::UniquePtr<PairDeviceParams> params(reinterpret_cast<PairDeviceParams *>(context));
+    PairingManager & self = PairingManager::Instance();
+
+    self.InitPairingCommand();
+    self.mSetupPINCode = params->setupPINCode;
+
+    Inet::IPAddress address;
+    Inet::InterfaceId interfaceId;
+
+    if (!ParseAddressWithInterface(params->ipAddrBuffer, address, interfaceId))
+    {
+        ChipLogError(NotSpecified, "Invalid IP address: %s", params->ipAddrBuffer);
+        return;
+    }
+
+    CHIP_ERROR err = self.Pair(params->nodeId, Transport::PeerAddress::UDP(address, params->deviceRemotePort, interfaceId));
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to pair device, error: %s", ErrorStr(err));
+    }
+}
+
+CHIP_ERROR PairingManager::UnpairDevice(NodeId nodeId)
+{
+    auto params = Platform::MakeUnique<UnpairDeviceParams>();
+    VerifyOrReturnError(params != nullptr, CHIP_ERROR_NO_MEMORY);
+
+    params->nodeId = nodeId;
+
+    // Schedule work on the Matter thread
+    return DeviceLayer::PlatformMgr().ScheduleWork(OnUnpairDevice, reinterpret_cast<intptr_t>(params.release()));
+}
+
+void PairingManager::OnUnpairDevice(intptr_t context)
+{
+    Platform::UniquePtr<PairDeviceParams> params(reinterpret_cast<PairDeviceParams *>(context));
+    PairingManager & self = PairingManager::Instance();
+
+    self.InitPairingCommand();
+
+    self.mCurrentFabricRemover = Platform::MakeUnique<Controller::CurrentFabricRemover>(self.mCommissioner);
+
+    if (!self.mCurrentFabricRemover)
+    {
+        ChipLogError(NotSpecified, "Failed to unpair device, mCurrentFabricRemover is null");
+        return;
+    }
+
+    CHIP_ERROR err = self.mCurrentFabricRemover->RemoveCurrentFabric(params->nodeId, &self.mCurrentFabricRemoveCallback);
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to unpair device, error: %s", ErrorStr(err));
+    }
+}
diff --git a/examples/fabric-admin/device_manager/PairingManager.h b/examples/fabric-admin/device_manager/PairingManager.h
index de1c088..563d129 100644
--- a/examples/fabric-admin/device_manager/PairingManager.h
+++ b/examples/fabric-admin/device_manager/PairingManager.h
@@ -18,10 +18,16 @@
 
 #pragma once
 
+#include <commands/common/CredentialIssuerCommands.h>
 #include <controller/CHIPDeviceController.h>
+#include <controller/CommissioningDelegate.h>
 #include <controller/CommissioningWindowOpener.h>
+#include <controller/CurrentFabricRemover.h>
 #include <crypto/CHIPCryptoPAL.h>
 
+// Constants
+constexpr uint16_t kMaxManualCodeLength = 22;
+
 class CommissioningWindowDelegate
 {
 public:
@@ -29,6 +35,20 @@
     virtual ~CommissioningWindowDelegate()                                                                      = default;
 };
 
+class CommissioningDelegate
+{
+public:
+    virtual void OnCommissioningComplete(chip::NodeId deviceId, CHIP_ERROR err) = 0;
+    virtual ~CommissioningDelegate()                                            = default;
+};
+
+class PairingDelegate
+{
+public:
+    virtual void OnDeviceRemoved(chip::NodeId deviceId, CHIP_ERROR err) = 0;
+    virtual ~PairingDelegate()                                          = default;
+};
+
 /**
  * The PairingManager class is responsible for managing the commissioning and pairing process
  * of Matter devices. PairingManager is designed to be used as a singleton, meaning that there
@@ -49,7 +69,9 @@
  * manager.OpenCommissioningWindow();
  * @endcode
  */
-class PairingManager
+class PairingManager : public chip::Controller::DevicePairingDelegate,
+                       public chip::Controller::DeviceDiscoveryDelegate,
+                       public chip::Credentials::DeviceAttestationDelegate
 {
 public:
     static PairingManager & Instance()
@@ -58,7 +80,14 @@
         return instance;
     }
 
-    void Init(chip::Controller::DeviceCommissioner * commissioner);
+    CHIP_ERROR Init(chip::Controller::DeviceCommissioner * commissioner, CredentialIssuerCommands * credIssuerCmds);
+
+    void SetOpenCommissioningWindowDelegate(CommissioningWindowDelegate * delegate) { mCommissioningWindowDelegate = delegate; }
+    void SetCommissioningDelegate(CommissioningDelegate * delegate) { mCommissioningDelegate = delegate; }
+    void SetPairingDelegate(PairingDelegate * delegate) { mPairingDelegate = delegate; }
+    PairingDelegate * GetPairingDelegate() { return mPairingDelegate; }
+
+    chip::Controller::DeviceCommissioner & CurrentCommissioner() { return *mCommissioner; };
 
     /**
      * Opens a commissioning window on the specified node and endpoint.
@@ -79,16 +108,38 @@
                                        uint32_t iterations, uint16_t discriminator, const chip::ByteSpan & salt,
                                        const chip::ByteSpan & verifier);
 
-    void SetOpenCommissioningWindowDelegate(CommissioningWindowDelegate * delegate) { mCommissioningWindowDelegate = delegate; }
+    /**
+     * Pairs a device using a setup code payload.
+     *
+     * @param nodeId The target node ID for pairing.
+     * @param payload The setup code payload, which typically contains device-specific pairing information.
+     *
+     * @return CHIP_NO_ERROR on successful initiation of the pairing process, or an appropriate CHIP_ERROR if pairing fails.
+     */
+    CHIP_ERROR PairDeviceWithCode(chip::NodeId nodeId, const char * payload);
+
+    /**
+     * Pairs a device using its setup PIN code and remote IP address.
+     *
+     * @param nodeId The target node ID for pairing.
+     * @param setupPINCode The setup PIN code for the device, used for establishing a secure connection.
+     * @param deviceRemoteIp The IP address of the remote device.
+     * @param deviceRemotePort The port number on which the device is listening for pairing requests.
+     *
+     * @return CHIP_NO_ERROR if the pairing process is initiated successfully, or an appropriate CHIP_ERROR if pairing fails.
+     */
+    CHIP_ERROR PairDevice(chip::NodeId nodeId, uint32_t setupPINCode, const char * deviceRemoteIp, uint16_t deviceRemotePort);
+
+    /**
+     * Unpairs a device with the specified node ID.
+     *
+     * @param nodeId The node ID of the device to be unpaired.
+     *
+     * @return CHIP_NO_ERROR if the device is successfully unpaired, or an appropriate CHIP_ERROR if the process fails.
+     */
+    CHIP_ERROR UnpairDevice(chip::NodeId nodeId);
 
 private:
-    PairingManager();
-    PairingManager(const PairingManager &)             = delete;
-    PairingManager & operator=(const PairingManager &) = delete;
-
-    chip::Controller::DeviceCommissioner * mCommissioner = nullptr;
-
-    /////////// Open Commissioning Window Command Interface /////////
     struct CommissioningWindowParams
     {
         chip::NodeId nodeId;
@@ -103,7 +154,84 @@
         chip::ByteSpan salt;
     };
 
+    struct PairDeviceWithCodeParams
+    {
+        chip::NodeId nodeId;
+        char payloadBuffer[kMaxManualCodeLength + 1];
+    };
+
+    struct PairDeviceParams
+    {
+        chip::NodeId nodeId;
+        uint32_t setupPINCode;
+        uint16_t deviceRemotePort;
+        char ipAddrBuffer[chip::Inet::IPAddress::kMaxStringLength];
+    };
+
+    struct UnpairDeviceParams
+    {
+        chip::NodeId nodeId;
+    };
+
+    // Constructors
+    PairingManager();
+    PairingManager(const PairingManager &)             = delete;
+    PairingManager & operator=(const PairingManager &) = delete;
+
+    // Private member functions (static and non-static)
+    chip::Controller::CommissioningParameters GetCommissioningParameters();
+    void InitPairingCommand();
+    CHIP_ERROR Pair(chip::NodeId remoteId, chip::Transport::PeerAddress address);
+
+    /////////// DevicePairingDelegate Interface /////////
+    void OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status) override;
+    void OnPairingComplete(CHIP_ERROR error) override;
+    void OnPairingDeleted(CHIP_ERROR error) override;
+    void OnReadCommissioningInfo(const chip::Controller::ReadCommissioningInfo & info) override;
+    void OnCommissioningComplete(chip::NodeId deviceId, CHIP_ERROR error) override;
+    void OnICDRegistrationComplete(chip::ScopedNodeId deviceId, uint32_t icdCounter) override;
+    void OnICDStayActiveComplete(chip::ScopedNodeId deviceId, uint32_t promisedActiveDuration) override;
+
+    /////////// DeviceDiscoveryDelegate Interface /////////
+    void OnDiscoveredDevice(const chip::Dnssd::CommissionNodeData & nodeData) override;
+
+    /////////// DeviceAttestationDelegate Interface /////////
+    chip::Optional<uint16_t> FailSafeExpiryTimeoutSecs() const override;
+    bool ShouldWaitAfterDeviceAttestation() override;
+    void OnDeviceAttestationCompleted(chip::Controller::DeviceCommissioner * deviceCommissioner, chip::DeviceProxy * device,
+                                      const chip::Credentials::DeviceAttestationVerifier::AttestationDeviceInfo & info,
+                                      chip::Credentials::AttestationVerificationResult attestationResult) override;
+
+    static void OnOpenCommissioningWindow(intptr_t context);
+    static void OnOpenCommissioningWindowResponse(void * context, chip::NodeId deviceId, CHIP_ERROR status,
+                                                  chip::SetupPayload payload);
+    static void OnOpenCommissioningWindowVerifierResponse(void * context, chip::NodeId deviceId, CHIP_ERROR status);
+    static void OnCurrentFabricRemove(void * context, chip::NodeId remoteNodeId, CHIP_ERROR status);
+    static void OnPairDeviceWithCode(intptr_t context);
+    static void OnPairDevice(intptr_t context);
+    static void OnUnpairDevice(intptr_t context);
+
+    // Private data members
+    chip::Controller::DeviceCommissioner * mCommissioner = nullptr;
+    CredentialIssuerCommands * mCredIssuerCmds           = nullptr;
+
     CommissioningWindowDelegate * mCommissioningWindowDelegate = nullptr;
+    CommissioningDelegate * mCommissioningDelegate             = nullptr;
+    PairingDelegate * mPairingDelegate                         = nullptr;
+
+    chip::NodeId mNodeId            = chip::kUndefinedNodeId;
+    uint16_t mDiscriminator         = 0;
+    uint32_t mSetupPINCode          = 0;
+    const char * mOnboardingPayload = nullptr;
+    bool mDeviceIsICD               = false;
+    uint8_t mRandomGeneratedICDSymmetricKey[chip::Crypto::kAES_CCM128_Key_Length];
+
+    chip::Optional<bool> mICDRegistration;
+    chip::Optional<chip::NodeId> mICDCheckInNodeId;
+    chip::Optional<chip::app::Clusters::IcdManagement::ClientTypeEnum> mICDClientType;
+    chip::Optional<chip::ByteSpan> mICDSymmetricKey;
+    chip::Optional<uint64_t> mICDMonitoredSubject;
+    chip::Optional<uint32_t> mICDStayActiveDurationMsec;
 
     /**
      * Holds the unique_ptr to the current CommissioningWindowOpener.
@@ -111,12 +239,10 @@
      * The pointer is reset when the commissioning window is closed or when an error occurs.
      */
     chip::Platform::UniquePtr<chip::Controller::CommissioningWindowOpener> mWindowOpener;
-
-    static void OnOpenCommissioningWindow(intptr_t context);
-    static void OnOpenCommissioningWindowResponse(void * context, chip::NodeId deviceId, CHIP_ERROR status,
-                                                  chip::SetupPayload payload);
-    static void OnOpenCommissioningWindowVerifierResponse(void * context, chip::NodeId deviceId, CHIP_ERROR status);
-
     chip::Callback::Callback<chip::Controller::OnOpenCommissioningWindow> mOnOpenCommissioningWindowCallback;
     chip::Callback::Callback<chip::Controller::OnOpenCommissioningWindowWithVerifier> mOnOpenCommissioningWindowVerifierCallback;
+
+    // For Unpair
+    chip::Platform::UniquePtr<chip::Controller::CurrentFabricRemover> mCurrentFabricRemover;
+    chip::Callback::Callback<chip::Controller::OnCurrentFabricRemove> mCurrentFabricRemoveCallback;
 };