Skip to content

Commit 150db87

Browse files
authored
[Fabric-Admin] Refactor to use API methods instead of PushCommand (3/3) (project-chip#36033)
* [Fabric-Admin] Implement the bridge subscription API * Restyle * Address review comments * Reset the subscription state only in OnDone * Keep subscription
1 parent 1000d7e commit 150db87

File tree

6 files changed

+250
-20
lines changed

6 files changed

+250
-20
lines changed

examples/fabric-admin/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ static_library("fabric-admin-utils") {
8080
"commands/pairing/OpenCommissioningWindowCommand.h",
8181
"commands/pairing/PairingCommand.cpp",
8282
"commands/pairing/ToTLVCert.cpp",
83+
"device_manager/BridgeSubscription.cpp",
84+
"device_manager/BridgeSubscription.h",
8385
"device_manager/DeviceManager.cpp",
8486
"device_manager/DeviceManager.h",
8587
"device_manager/DeviceSubscription.cpp",

examples/fabric-admin/commands/clusters/ReportCommand.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,4 @@ void ReportCommand::OnEventData(const app::EventHeader & eventHeader, TLV::TLVRe
7272
}
7373

7474
LogErrorOnFailure(RemoteDataModelLogger::LogEventAsJSON(eventHeader, data));
75-
76-
DeviceMgr().HandleEventData(eventHeader, *data);
7775
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* Copyright (c) 2024 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
#include "BridgeSubscription.h"
20+
#include <device_manager/DeviceManager.h>
21+
22+
using namespace ::chip;
23+
using namespace ::chip::app;
24+
using chip::app::ReadClient;
25+
26+
namespace {
27+
28+
constexpr uint16_t kSubscribeMinInterval = 0;
29+
constexpr uint16_t kSubscribeMaxInterval = 60;
30+
31+
void OnDeviceConnectedWrapper(void * context, Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
32+
{
33+
reinterpret_cast<BridgeSubscription *>(context)->OnDeviceConnected(exchangeMgr, sessionHandle);
34+
}
35+
36+
void OnDeviceConnectionFailureWrapper(void * context, const ScopedNodeId & peerId, CHIP_ERROR error)
37+
{
38+
reinterpret_cast<BridgeSubscription *>(context)->OnDeviceConnectionFailure(peerId, error);
39+
}
40+
41+
} // namespace
42+
43+
BridgeSubscription::BridgeSubscription() :
44+
mOnDeviceConnectedCallback(OnDeviceConnectedWrapper, this),
45+
mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureWrapper, this)
46+
{}
47+
48+
CHIP_ERROR BridgeSubscription::StartSubscription(Controller::DeviceController & controller, NodeId nodeId, EndpointId endpointId)
49+
{
50+
assertChipStackLockedByCurrentThread();
51+
52+
VerifyOrDie(!subscriptionStarted); // Ensure it's not called multiple times.
53+
54+
// Mark as started
55+
subscriptionStarted = true;
56+
57+
mEndpointId = endpointId;
58+
59+
CHIP_ERROR err = controller.GetConnectedDevice(nodeId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
60+
if (err != CHIP_NO_ERROR)
61+
{
62+
ChipLogError(NotSpecified, "Failed to connect to remote fabric sync bridge %" CHIP_ERROR_FORMAT, err.Format());
63+
}
64+
return err;
65+
}
66+
67+
void BridgeSubscription::OnAttributeData(const ConcreteDataAttributePath & path, TLV::TLVReader * data, const StatusIB & status)
68+
{
69+
if (!status.IsSuccess())
70+
{
71+
ChipLogError(NotSpecified, "Response Failure: %" CHIP_ERROR_FORMAT, status.ToChipError().Format());
72+
return;
73+
}
74+
75+
if (data == nullptr)
76+
{
77+
ChipLogError(NotSpecified, "Response Failure: No Data");
78+
return;
79+
}
80+
81+
DeviceMgr().HandleAttributeData(path, *data);
82+
}
83+
84+
void BridgeSubscription::OnEventData(const app::EventHeader & eventHeader, TLV::TLVReader * data, const app::StatusIB * status)
85+
{
86+
if (status != nullptr)
87+
{
88+
CHIP_ERROR error = status->ToChipError();
89+
if (CHIP_NO_ERROR != error)
90+
{
91+
ChipLogError(NotSpecified, "Response Failure: %" CHIP_ERROR_FORMAT, error.Format());
92+
return;
93+
}
94+
}
95+
96+
if (data == nullptr)
97+
{
98+
ChipLogError(NotSpecified, "Response Failure: No Data");
99+
return;
100+
}
101+
102+
DeviceMgr().HandleEventData(eventHeader, *data);
103+
}
104+
105+
void BridgeSubscription::OnError(CHIP_ERROR error)
106+
{
107+
ChipLogProgress(NotSpecified, "Error on remote fabric sync bridge subscription: %" CHIP_ERROR_FORMAT, error.Format());
108+
}
109+
110+
void BridgeSubscription::OnDone(ReadClient * apReadClient)
111+
{
112+
mClient.reset();
113+
ChipLogProgress(NotSpecified, "The remote fabric sync bridge subscription is terminated");
114+
115+
// Reset the subscription state to allow retry
116+
subscriptionStarted = false;
117+
118+
// TODO:(#36092) Fabric-Admin should attempt to re-subscribe when the subscription to the remote bridge is terminated.
119+
}
120+
121+
void BridgeSubscription::OnDeviceConnected(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
122+
{
123+
mClient = std::make_unique<ReadClient>(app::InteractionModelEngine::GetInstance(), &exchangeMgr /* echangeMgr */,
124+
*this /* callback */, ReadClient::InteractionType::Subscribe);
125+
VerifyOrDie(mClient);
126+
127+
AttributePathParams readPaths[1];
128+
readPaths[0] = AttributePathParams(mEndpointId, Clusters::Descriptor::Id, Clusters::Descriptor::Attributes::PartsList::Id);
129+
130+
EventPathParams eventPaths[1];
131+
eventPaths[0] = EventPathParams(mEndpointId, Clusters::CommissionerControl::Id,
132+
Clusters::CommissionerControl::Events::CommissioningRequestResult::Id);
133+
eventPaths[0].mIsUrgentEvent = true;
134+
135+
ReadPrepareParams readParams(sessionHandle);
136+
137+
readParams.mpAttributePathParamsList = readPaths;
138+
readParams.mAttributePathParamsListSize = 1;
139+
readParams.mpEventPathParamsList = eventPaths;
140+
readParams.mEventPathParamsListSize = 1;
141+
readParams.mMinIntervalFloorSeconds = kSubscribeMinInterval;
142+
readParams.mMaxIntervalCeilingSeconds = kSubscribeMaxInterval;
143+
readParams.mKeepSubscriptions = true;
144+
145+
CHIP_ERROR err = mClient->SendRequest(readParams);
146+
147+
if (err != CHIP_NO_ERROR)
148+
{
149+
ChipLogError(NotSpecified, "Failed to issue subscription to the Descriptor Cluster of the remote bridged device.");
150+
OnDone(nullptr);
151+
return;
152+
}
153+
}
154+
155+
void BridgeSubscription::OnDeviceConnectionFailure(const ScopedNodeId & peerId, CHIP_ERROR error)
156+
{
157+
ChipLogError(NotSpecified, "BridgeSubscription failed to connect to " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId()));
158+
OnDone(nullptr);
159+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright (c) 2024 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
#pragma once
20+
21+
#include <app/ReadClient.h>
22+
#include <controller/CHIPDeviceController.h>
23+
24+
#include <memory>
25+
#include <optional>
26+
27+
/**
28+
* @brief Class used to subscribe to attributes and events from the remote bridged device.
29+
*
30+
* The Descriptor Cluster contains attributes such as the Parts List, which provides a list
31+
* of endpoints or devices that are part of a composite device or bridge. The CommissionerControl
32+
* Cluster generates events related to commissioning requests, which can be monitored to track
33+
* device commissioning status.
34+
*
35+
* When subscribing to attributes and events of a bridged device from another fabric, the class:
36+
* - Establishes a secure session with the device (if needed) via CASE (Chip over
37+
* Authenticated Session Establishment) session.
38+
* - Subscribes to the specified attributes in the Descriptor Cluster (e.g., Parts List) and
39+
* events in the CommissionerControl Cluster (e.g., CommissioningRequestResult) of the remote
40+
* device on the specified node and endpoint.
41+
* - Invokes the provided callback upon successful or unsuccessful subscription, allowing
42+
* further handling of data or errors.
43+
*
44+
* This class also implements the necessary callbacks to handle attribute data reports, event data,
45+
* errors, and session establishment procedures.
46+
*/
47+
class BridgeSubscription : public chip::app::ReadClient::Callback
48+
{
49+
public:
50+
BridgeSubscription();
51+
52+
CHIP_ERROR StartSubscription(chip::Controller::DeviceController & controller, chip::NodeId nodeId, chip::EndpointId endpointId);
53+
54+
///////////////////////////////////////////////////////////////
55+
// ReadClient::Callback implementation
56+
///////////////////////////////////////////////////////////////
57+
void OnAttributeData(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data,
58+
const chip::app::StatusIB & status) override;
59+
void OnEventData(const chip::app::EventHeader & eventHeader, chip::TLV::TLVReader * data,
60+
const chip::app::StatusIB * status) override;
61+
void OnError(CHIP_ERROR error) override;
62+
void OnDone(chip::app::ReadClient * apReadClient) override;
63+
64+
///////////////////////////////////////////////////////////////
65+
// callbacks for CASE session establishment
66+
///////////////////////////////////////////////////////////////
67+
void OnDeviceConnected(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle);
68+
void OnDeviceConnectionFailure(const chip::ScopedNodeId & peerId, CHIP_ERROR error);
69+
70+
private:
71+
std::unique_ptr<chip::app::ReadClient> mClient;
72+
73+
chip::Callback::Callback<chip::OnDeviceConnected> mOnDeviceConnectedCallback;
74+
chip::Callback::Callback<chip::OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;
75+
chip::EndpointId mEndpointId;
76+
bool subscriptionStarted = false;
77+
};

examples/fabric-admin/device_manager/DeviceManager.cpp

+9-18
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@ namespace {
3333
constexpr EndpointId kAggregatorEndpointId = 1;
3434
constexpr uint16_t kWindowTimeout = 300;
3535
constexpr uint16_t kIteration = 1000;
36-
constexpr uint16_t kSubscribeMinInterval = 0;
37-
constexpr uint16_t kSubscribeMaxInterval = 60;
38-
constexpr uint16_t kAggragatorEndpointId = 1;
3936
constexpr uint16_t kMaxDiscriminatorLength = 4095;
4037

4138
} // namespace
@@ -193,23 +190,17 @@ void DeviceManager::UnpairLocalFabricBridge()
193190

194191
void DeviceManager::SubscribeRemoteFabricBridge()
195192
{
196-
// Listen to the state changes of the remote fabric bridge.
197-
StringBuilder<kMaxCommandSize> commandBuilder;
198-
199-
// Prepare and push the descriptor subscribe command
200-
commandBuilder.Add("descriptor subscribe parts-list ");
201-
commandBuilder.AddFormat("%d %d %lu %d", kSubscribeMinInterval, kSubscribeMaxInterval, mRemoteBridgeNodeId,
202-
kAggragatorEndpointId);
203-
PushCommand(commandBuilder.c_str());
193+
ChipLogProgress(NotSpecified, "Start subscription to the remote bridge.")
204194

205-
// Clear the builder for the next command
206-
commandBuilder.Reset();
195+
CHIP_ERROR error = mBridgeSubscriber.StartSubscription(PairingManager::Instance().CurrentCommissioner(),
196+
mRemoteBridgeNodeId, kAggregatorEndpointId);
207197

208-
// Prepare and push the commissioner control subscribe command
209-
commandBuilder.Add("commissionercontrol subscribe-event commissioning-request-result ");
210-
commandBuilder.AddFormat("%d %d %lu %d --is-urgent true --keepSubscriptions true", kSubscribeMinInterval, kSubscribeMaxInterval,
211-
mRemoteBridgeNodeId, kAggregatorEndpointId);
212-
PushCommand(commandBuilder.c_str());
198+
if (error != CHIP_NO_ERROR)
199+
{
200+
ChipLogError(NotSpecified, "Failed to subscribe to the remote bridge (NodeId: %lu). Error: %" CHIP_ERROR_FORMAT,
201+
mRemoteBridgeNodeId, error.Format());
202+
return;
203+
}
213204
}
214205

215206
void DeviceManager::ReadSupportedDeviceCategories()

examples/fabric-admin/device_manager/DeviceManager.h

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#pragma once
2020

2121
#include <app-common/zap-generated/cluster-objects.h>
22+
#include <device_manager/BridgeSubscription.h>
2223
#include <device_manager/PairingManager.h>
2324
#include <platform/CHIPDeviceLayer.h>
2425

@@ -209,6 +210,8 @@ class DeviceManager : public PairingDelegate
209210
bool mAutoSyncEnabled = false;
210211
bool mInitialized = false;
211212
uint64_t mRequestId = 0;
213+
214+
BridgeSubscription mBridgeSubscriber;
212215
};
213216

214217
/**

0 commit comments

Comments
 (0)