[Fabric-Admin] Implement FabricSyncGetter APIs (#36108)

* [Fabric-Admin] Implement FabricSyncGetter APIs

* Update examples/fabric-admin/device_manager/FabricSyncGetter.h

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

---------

Co-authored-by: Terence Hampson <thampson@google.com>
diff --git a/examples/fabric-admin/BUILD.gn b/examples/fabric-admin/BUILD.gn
index 38252cd..58d9cee 100644
--- a/examples/fabric-admin/BUILD.gn
+++ b/examples/fabric-admin/BUILD.gn
@@ -90,6 +90,8 @@
     "device_manager/DeviceSubscriptionManager.h",
     "device_manager/DeviceSynchronization.cpp",
     "device_manager/DeviceSynchronization.h",
+    "device_manager/FabricSyncGetter.cpp",
+    "device_manager/FabricSyncGetter.h",
     "device_manager/PairingManager.cpp",
     "device_manager/PairingManager.h",
     "device_manager/UniqueIdGetter.cpp",
diff --git a/examples/fabric-admin/commands/clusters/ReportCommand.cpp b/examples/fabric-admin/commands/clusters/ReportCommand.cpp
index df6f329..7f3decd 100644
--- a/examples/fabric-admin/commands/clusters/ReportCommand.cpp
+++ b/examples/fabric-admin/commands/clusters/ReportCommand.cpp
@@ -45,8 +45,6 @@
     }
 
     LogErrorOnFailure(RemoteDataModelLogger::LogAttributeAsJSON(path, data));
-
-    DeviceMgr().HandleAttributeData(path, *data);
 }
 
 void ReportCommand::OnEventData(const app::EventHeader & eventHeader, TLV::TLVReader * data, const app::StatusIB * status)
diff --git a/examples/fabric-admin/device_manager/DeviceManager.cpp b/examples/fabric-admin/device_manager/DeviceManager.cpp
index 2968bcf..3bfd677 100644
--- a/examples/fabric-admin/device_manager/DeviceManager.cpp
+++ b/examples/fabric-admin/device_manager/DeviceManager.cpp
@@ -212,13 +212,18 @@
         return;
     }
 
-    StringBuilder<kMaxCommandSize> commandBuilder;
+    ChipLogProgress(NotSpecified, "Read SupportedDeviceCategories from the remote bridge.");
 
-    commandBuilder.Add("commissionercontrol read supported-device-categories ");
-    commandBuilder.AddFormat("%ld ", mRemoteBridgeNodeId);
-    commandBuilder.AddFormat("%d", kAggregatorEndpointId);
+    CHIP_ERROR error = mFabricSyncGetter.GetFabricSynchronizationData(
+        [this](TLV::TLVReader & data) { this->HandleReadSupportedDeviceCategories(data); },
+        PairingManager::Instance().CurrentCommissioner(), this->GetRemoteBridgeNodeId(), kAggregatorEndpointId);
 
-    PushCommand(commandBuilder.c_str());
+    if (error != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified,
+                     "Failed to read SupportedDeviceCategories from the remote bridge (NodeId: %lu). Error: %" CHIP_ERROR_FORMAT,
+                     mRemoteBridgeNodeId, error.Format());
+    }
 }
 
 void DeviceManager::HandleReadSupportedDeviceCategories(TLV::TLVReader & data)
@@ -421,13 +426,6 @@
 
 void DeviceManager::HandleAttributeData(const app::ConcreteDataAttributePath & path, TLV::TLVReader & data)
 {
-    if (path.mClusterId == CommissionerControl::Id &&
-        path.mAttributeId == CommissionerControl::Attributes::SupportedDeviceCategories::Id)
-    {
-        HandleReadSupportedDeviceCategories(data);
-        return;
-    }
-
     if (path.mClusterId == Descriptor::Id && path.mAttributeId == Descriptor::Attributes::PartsList::Id)
     {
         HandleAttributePartsListUpdate(data);
diff --git a/examples/fabric-admin/device_manager/DeviceManager.h b/examples/fabric-admin/device_manager/DeviceManager.h
index 62d5ae0..43461f4 100644
--- a/examples/fabric-admin/device_manager/DeviceManager.h
+++ b/examples/fabric-admin/device_manager/DeviceManager.h
@@ -20,6 +20,7 @@
 
 #include <app-common/zap-generated/cluster-objects.h>
 #include <device_manager/BridgeSubscription.h>
+#include <device_manager/FabricSyncGetter.h>
 #include <device_manager/PairingManager.h>
 #include <platform/CHIPDeviceLayer.h>
 
@@ -212,6 +213,7 @@
     uint64_t mRequestId   = 0;
 
     BridgeSubscription mBridgeSubscriber;
+    FabricSyncGetter mFabricSyncGetter;
 };
 
 /**
diff --git a/examples/fabric-admin/device_manager/FabricSyncGetter.cpp b/examples/fabric-admin/device_manager/FabricSyncGetter.cpp
new file mode 100644
index 0000000..5201048
--- /dev/null
+++ b/examples/fabric-admin/device_manager/FabricSyncGetter.cpp
@@ -0,0 +1,121 @@
+/*
+ *   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 "FabricSyncGetter.h"
+
+using namespace ::chip;
+using namespace ::chip::app;
+using chip::app::ReadClient;
+
+namespace {
+
+void OnDeviceConnectedWrapper(void * context, Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
+{
+    reinterpret_cast<FabricSyncGetter *>(context)->OnDeviceConnected(exchangeMgr, sessionHandle);
+}
+
+void OnDeviceConnectionFailureWrapper(void * context, const ScopedNodeId & peerId, CHIP_ERROR error)
+{
+    reinterpret_cast<FabricSyncGetter *>(context)->OnDeviceConnectionFailure(peerId, error);
+}
+
+} // namespace
+
+FabricSyncGetter::FabricSyncGetter() :
+    mOnDeviceConnectedCallback(OnDeviceConnectedWrapper, this),
+    mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureWrapper, this)
+{}
+
+CHIP_ERROR FabricSyncGetter::GetFabricSynchronizationData(OnDoneCallback onDoneCallback, Controller::DeviceController & controller,
+                                                          NodeId nodeId, EndpointId endpointId)
+{
+    assertChipStackLockedByCurrentThread();
+
+    mEndpointId     = endpointId;
+    mOnDoneCallback = onDoneCallback;
+
+    CHIP_ERROR err = controller.GetConnectedDevice(nodeId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to connect to remote fabric bridge %" CHIP_ERROR_FORMAT, err.Format());
+    }
+    return err;
+}
+
+void FabricSyncGetter::OnAttributeData(const ConcreteDataAttributePath & path, TLV::TLVReader * data, const StatusIB & status)
+{
+    VerifyOrDie(path.mClusterId == Clusters::CommissionerControl::Id);
+
+    if (!status.IsSuccess())
+    {
+        ChipLogError(NotSpecified, "Response Failure: %" CHIP_ERROR_FORMAT, status.ToChipError().Format());
+        return;
+    }
+
+    switch (path.mAttributeId)
+    {
+    case Clusters::CommissionerControl::Attributes::SupportedDeviceCategories::Id: {
+        mOnDoneCallback(*data);
+        break;
+    }
+    default:
+        break;
+    }
+}
+
+void FabricSyncGetter::OnError(CHIP_ERROR error)
+{
+    ChipLogProgress(NotSpecified, "Error Getting SupportedDeviceCategories: %" CHIP_ERROR_FORMAT, error.Format());
+}
+
+void FabricSyncGetter::OnDone(ReadClient * apReadClient)
+{
+    ChipLogProgress(NotSpecified, "Reading SupportedDeviceCategories is done.");
+}
+
+void FabricSyncGetter::OnDeviceConnected(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
+{
+    mClient = std::make_unique<ReadClient>(app::InteractionModelEngine::GetInstance(), &exchangeMgr, *this /* callback */,
+                                           ReadClient::InteractionType::Read);
+    VerifyOrDie(mClient);
+
+    AttributePathParams readPaths[1];
+    readPaths[0] = AttributePathParams(mEndpointId, Clusters::CommissionerControl::Id,
+                                       Clusters::CommissionerControl::Attributes::SupportedDeviceCategories::Id);
+
+    ReadPrepareParams readParams(sessionHandle);
+
+    readParams.mpAttributePathParamsList    = readPaths;
+    readParams.mAttributePathParamsListSize = 1;
+
+    CHIP_ERROR err = mClient->SendRequest(readParams);
+
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogError(NotSpecified, "Failed to read SupportedDeviceCategories from the bridged device.");
+        OnDone(nullptr);
+        return;
+    }
+}
+
+void FabricSyncGetter::OnDeviceConnectionFailure(const ScopedNodeId & peerId, CHIP_ERROR error)
+{
+    ChipLogError(NotSpecified, "FabricSyncGetter failed to connect to " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId()));
+
+    OnDone(nullptr);
+}
diff --git a/examples/fabric-admin/device_manager/FabricSyncGetter.h b/examples/fabric-admin/device_manager/FabricSyncGetter.h
new file mode 100644
index 0000000..d6d3a5b
--- /dev/null
+++ b/examples/fabric-admin/device_manager/FabricSyncGetter.h
@@ -0,0 +1,75 @@
+/*
+ *   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 get FabricSynchronization from SupportedDeviceCategories attribute of Commissioner Control Cluster.
+ *
+ * Functionality:
+ *  - Establishes a CASE session to communicate with the remote bridge.
+ *  - Retrieves the attribute data from the endpoint which host Aggregator.
+ *  - Provides callbacks for success, error, and completion when retrieving data.
+ */
+class FabricSyncGetter : public chip::app::ReadClient::Callback
+{
+public:
+    using OnDoneCallback = std::function<void(chip::TLV::TLVReader & data)>;
+
+    FabricSyncGetter();
+
+    /**
+     * @brief Initiates the process of retrieving fabric synchronization data from the target device.
+     *
+     * @param onDoneCallback A callback function to be invoked when the data retrieval is complete.
+     * @param controller The device controller used to establish a session with the target device.
+     * @param nodeId The Node ID of the target device.
+     * @param endpointId The Endpoint ID from which to retrieve the fabric synchronization data.
+     * @return CHIP_ERROR Returns an error if the process fails, CHIP_NO_ERROR on success.
+     */
+    CHIP_ERROR GetFabricSynchronizationData(OnDoneCallback onDoneCallback, 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 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;
+
+    OnDoneCallback mOnDoneCallback;
+    chip::EndpointId mEndpointId;
+    chip::Callback::Callback<chip::OnDeviceConnected> mOnDeviceConnectedCallback;
+    chip::Callback::Callback<chip::OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;
+};