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

* [Fabric-Admin] Implement the bridge subscription API

* Restyle

* Address review comments

* Reset the subscription state only in OnDone

* Keep subscription
diff --git a/examples/fabric-admin/BUILD.gn b/examples/fabric-admin/BUILD.gn
index ab58445..38252cd 100644
--- a/examples/fabric-admin/BUILD.gn
+++ b/examples/fabric-admin/BUILD.gn
@@ -80,6 +80,8 @@
     "commands/pairing/OpenCommissioningWindowCommand.h",
     "commands/pairing/PairingCommand.cpp",
     "commands/pairing/ToTLVCert.cpp",
+    "device_manager/BridgeSubscription.cpp",
+    "device_manager/BridgeSubscription.h",
     "device_manager/DeviceManager.cpp",
     "device_manager/DeviceManager.h",
     "device_manager/DeviceSubscription.cpp",
diff --git a/examples/fabric-admin/commands/clusters/ReportCommand.cpp b/examples/fabric-admin/commands/clusters/ReportCommand.cpp
index 2fdb965..df6f329 100644
--- a/examples/fabric-admin/commands/clusters/ReportCommand.cpp
+++ b/examples/fabric-admin/commands/clusters/ReportCommand.cpp
@@ -72,6 +72,4 @@
     }
 
     LogErrorOnFailure(RemoteDataModelLogger::LogEventAsJSON(eventHeader, data));
-
-    DeviceMgr().HandleEventData(eventHeader, *data);
 }
diff --git a/examples/fabric-admin/device_manager/BridgeSubscription.cpp b/examples/fabric-admin/device_manager/BridgeSubscription.cpp
new file mode 100644
index 0000000..2efcada
--- /dev/null
+++ b/examples/fabric-admin/device_manager/BridgeSubscription.cpp
@@ -0,0 +1,159 @@
+/*
+ *   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 "BridgeSubscription.h"
+#include <device_manager/DeviceManager.h>
+
+using namespace ::chip;
+using namespace ::chip::app;
+using chip::app::ReadClient;
+
+namespace {
+
+constexpr uint16_t kSubscribeMinInterval = 0;
+constexpr uint16_t kSubscribeMaxInterval = 60;
+
+void OnDeviceConnectedWrapper(void * context, Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
+{
+    reinterpret_cast<BridgeSubscription *>(context)->OnDeviceConnected(exchangeMgr, sessionHandle);
+}
+
+void OnDeviceConnectionFailureWrapper(void * context, const ScopedNodeId & peerId, CHIP_ERROR error)
+{
+    reinterpret_cast<BridgeSubscription *>(context)->OnDeviceConnectionFailure(peerId, error);
+}
+
+} // namespace
+
+BridgeSubscription::BridgeSubscription() :
+    mOnDeviceConnectedCallback(OnDeviceConnectedWrapper, this),
+    mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureWrapper, this)
+{}
+
+CHIP_ERROR BridgeSubscription::StartSubscription(Controller::DeviceController & controller, NodeId nodeId, EndpointId endpointId)
+{
+    assertChipStackLockedByCurrentThread();
+
+    VerifyOrDie(!subscriptionStarted); // Ensure it's not called multiple times.
+
+    // Mark as started
+    subscriptionStarted = true;
+
+    mEndpointId = endpointId;
+
+    CHIP_ERROR err = controller.GetConnectedDevice(nodeId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to connect to remote fabric sync bridge %" CHIP_ERROR_FORMAT, err.Format());
+    }
+    return err;
+}
+
+void BridgeSubscription::OnAttributeData(const ConcreteDataAttributePath & path, TLV::TLVReader * data, const StatusIB & status)
+{
+    if (!status.IsSuccess())
+    {
+        ChipLogError(NotSpecified, "Response Failure: %" CHIP_ERROR_FORMAT, status.ToChipError().Format());
+        return;
+    }
+
+    if (data == nullptr)
+    {
+        ChipLogError(NotSpecified, "Response Failure: No Data");
+        return;
+    }
+
+    DeviceMgr().HandleAttributeData(path, *data);
+}
+
+void BridgeSubscription::OnEventData(const app::EventHeader & eventHeader, TLV::TLVReader * data, const app::StatusIB * status)
+{
+    if (status != nullptr)
+    {
+        CHIP_ERROR error = status->ToChipError();
+        if (CHIP_NO_ERROR != error)
+        {
+            ChipLogError(NotSpecified, "Response Failure: %" CHIP_ERROR_FORMAT, error.Format());
+            return;
+        }
+    }
+
+    if (data == nullptr)
+    {
+        ChipLogError(NotSpecified, "Response Failure: No Data");
+        return;
+    }
+
+    DeviceMgr().HandleEventData(eventHeader, *data);
+}
+
+void BridgeSubscription::OnError(CHIP_ERROR error)
+{
+    ChipLogProgress(NotSpecified, "Error on remote fabric sync bridge subscription: %" CHIP_ERROR_FORMAT, error.Format());
+}
+
+void BridgeSubscription::OnDone(ReadClient * apReadClient)
+{
+    mClient.reset();
+    ChipLogProgress(NotSpecified, "The remote fabric sync bridge subscription is terminated");
+
+    // Reset the subscription state to allow retry
+    subscriptionStarted = false;
+
+    // TODO:(#36092) Fabric-Admin should attempt to re-subscribe when the subscription to the remote bridge is terminated.
+}
+
+void BridgeSubscription::OnDeviceConnected(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
+{
+    mClient = std::make_unique<ReadClient>(app::InteractionModelEngine::GetInstance(), &exchangeMgr /* echangeMgr */,
+                                           *this /* callback */, ReadClient::InteractionType::Subscribe);
+    VerifyOrDie(mClient);
+
+    AttributePathParams readPaths[1];
+    readPaths[0] = AttributePathParams(mEndpointId, Clusters::Descriptor::Id, Clusters::Descriptor::Attributes::PartsList::Id);
+
+    EventPathParams eventPaths[1];
+    eventPaths[0]                = EventPathParams(mEndpointId, Clusters::CommissionerControl::Id,
+                                                   Clusters::CommissionerControl::Events::CommissioningRequestResult::Id);
+    eventPaths[0].mIsUrgentEvent = true;
+
+    ReadPrepareParams readParams(sessionHandle);
+
+    readParams.mpAttributePathParamsList    = readPaths;
+    readParams.mAttributePathParamsListSize = 1;
+    readParams.mpEventPathParamsList        = eventPaths;
+    readParams.mEventPathParamsListSize     = 1;
+    readParams.mMinIntervalFloorSeconds     = kSubscribeMinInterval;
+    readParams.mMaxIntervalCeilingSeconds   = kSubscribeMaxInterval;
+    readParams.mKeepSubscriptions           = true;
+
+    CHIP_ERROR err = mClient->SendRequest(readParams);
+
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to issue subscription to the Descriptor Cluster of the remote bridged device.");
+        OnDone(nullptr);
+        return;
+    }
+}
+
+void BridgeSubscription::OnDeviceConnectionFailure(const ScopedNodeId & peerId, CHIP_ERROR error)
+{
+    ChipLogError(NotSpecified, "BridgeSubscription failed to connect to " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId()));
+    OnDone(nullptr);
+}
diff --git a/examples/fabric-admin/device_manager/BridgeSubscription.h b/examples/fabric-admin/device_manager/BridgeSubscription.h
new file mode 100644
index 0000000..bd2a702
--- /dev/null
+++ b/examples/fabric-admin/device_manager/BridgeSubscription.h
@@ -0,0 +1,77 @@
+/*
+ *   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.
+ *
+ */
+
+#pragma once
+
+#include <app/ReadClient.h>
+#include <controller/CHIPDeviceController.h>
+
+#include <memory>
+#include <optional>
+
+/**
+ * @brief Class used to subscribe to attributes and events from the remote bridged device.
+ *
+ * The Descriptor Cluster contains attributes such as the Parts List, which provides a list
+ * of endpoints or devices that are part of a composite device or bridge. The CommissionerControl
+ * Cluster generates events related to commissioning requests, which can be monitored to track
+ * device commissioning status.
+ *
+ * When subscribing to attributes and events of a bridged device from another fabric, the class:
+ *    - Establishes a secure session with the device (if needed) via CASE (Chip over
+ *      Authenticated Session Establishment) session.
+ *    - Subscribes to the specified attributes in the Descriptor Cluster (e.g., Parts List) and
+ *      events in the CommissionerControl Cluster (e.g., CommissioningRequestResult) of the remote
+ *      device on the specified node and endpoint.
+ *    - Invokes the provided callback upon successful or unsuccessful subscription, allowing
+ *      further handling of data or errors.
+ *
+ * This class also implements the necessary callbacks to handle attribute data reports, event data,
+ * errors, and session establishment procedures.
+ */
+class BridgeSubscription : public chip::app::ReadClient::Callback
+{
+public:
+    BridgeSubscription();
+
+    CHIP_ERROR StartSubscription(chip::Controller::DeviceController & controller, chip::NodeId nodeId, chip::EndpointId endpointId);
+
+    ///////////////////////////////////////////////////////////////
+    // ReadClient::Callback implementation
+    ///////////////////////////////////////////////////////////////
+    void OnAttributeData(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data,
+                         const chip::app::StatusIB & status) override;
+    void OnEventData(const chip::app::EventHeader & eventHeader, chip::TLV::TLVReader * data,
+                     const chip::app::StatusIB * status) override;
+    void OnError(CHIP_ERROR error) override;
+    void OnDone(chip::app::ReadClient * apReadClient) override;
+
+    ///////////////////////////////////////////////////////////////
+    // callbacks for CASE session establishment
+    ///////////////////////////////////////////////////////////////
+    void OnDeviceConnected(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle);
+    void OnDeviceConnectionFailure(const chip::ScopedNodeId & peerId, CHIP_ERROR error);
+
+private:
+    std::unique_ptr<chip::app::ReadClient> mClient;
+
+    chip::Callback::Callback<chip::OnDeviceConnected> mOnDeviceConnectedCallback;
+    chip::Callback::Callback<chip::OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;
+    chip::EndpointId mEndpointId;
+    bool subscriptionStarted = false;
+};
diff --git a/examples/fabric-admin/device_manager/DeviceManager.cpp b/examples/fabric-admin/device_manager/DeviceManager.cpp
index 3a27c68..2968bcf 100644
--- a/examples/fabric-admin/device_manager/DeviceManager.cpp
+++ b/examples/fabric-admin/device_manager/DeviceManager.cpp
@@ -33,9 +33,6 @@
 constexpr EndpointId kAggregatorEndpointId = 1;
 constexpr uint16_t kWindowTimeout          = 300;
 constexpr uint16_t kIteration              = 1000;
-constexpr uint16_t kSubscribeMinInterval   = 0;
-constexpr uint16_t kSubscribeMaxInterval   = 60;
-constexpr uint16_t kAggragatorEndpointId   = 1;
 constexpr uint16_t kMaxDiscriminatorLength = 4095;
 
 } // namespace
@@ -193,23 +190,17 @@
 
 void DeviceManager::SubscribeRemoteFabricBridge()
 {
-    // Listen to the state changes of the remote fabric bridge.
-    StringBuilder<kMaxCommandSize> commandBuilder;
+    ChipLogProgress(NotSpecified, "Start subscription to the remote bridge.")
 
-    // Prepare and push the descriptor subscribe command
-    commandBuilder.Add("descriptor subscribe parts-list ");
-    commandBuilder.AddFormat("%d %d %lu %d", kSubscribeMinInterval, kSubscribeMaxInterval, mRemoteBridgeNodeId,
-                             kAggragatorEndpointId);
-    PushCommand(commandBuilder.c_str());
+        CHIP_ERROR error = mBridgeSubscriber.StartSubscription(PairingManager::Instance().CurrentCommissioner(),
+                                                               mRemoteBridgeNodeId, kAggregatorEndpointId);
 
-    // Clear the builder for the next command
-    commandBuilder.Reset();
-
-    // Prepare and push the commissioner control subscribe command
-    commandBuilder.Add("commissionercontrol subscribe-event commissioning-request-result ");
-    commandBuilder.AddFormat("%d %d %lu %d --is-urgent true --keepSubscriptions true", kSubscribeMinInterval, kSubscribeMaxInterval,
-                             mRemoteBridgeNodeId, kAggregatorEndpointId);
-    PushCommand(commandBuilder.c_str());
+    if (error != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to subscribe to the remote bridge (NodeId: %lu). Error: %" CHIP_ERROR_FORMAT,
+                     mRemoteBridgeNodeId, error.Format());
+        return;
+    }
 }
 
 void DeviceManager::ReadSupportedDeviceCategories()
diff --git a/examples/fabric-admin/device_manager/DeviceManager.h b/examples/fabric-admin/device_manager/DeviceManager.h
index 1514c41..62d5ae0 100644
--- a/examples/fabric-admin/device_manager/DeviceManager.h
+++ b/examples/fabric-admin/device_manager/DeviceManager.h
@@ -19,6 +19,7 @@
 #pragma once
 
 #include <app-common/zap-generated/cluster-objects.h>
+#include <device_manager/BridgeSubscription.h>
 #include <device_manager/PairingManager.h>
 #include <platform/CHIPDeviceLayer.h>
 
@@ -209,6 +210,8 @@
     bool mAutoSyncEnabled = false;
     bool mInitialized     = false;
     uint64_t mRequestId   = 0;
+
+    BridgeSubscription mBridgeSubscriber;
 };
 
 /**