Line data Source code
1 : /*
2 : * Copyright (c) 2025 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 : #include "CodeDrivenDataModelProvider.h"
18 : #include <app/persistence/AttributePersistenceProvider.h>
19 : #include <app/server-cluster/ServerClusterContext.h>
20 : #include <app/server-cluster/ServerClusterInterface.h>
21 : #include <data-model-providers/codedriven/endpoint/EndpointInterface.h>
22 : #include <lib/core/CHIPError.h>
23 : #include <lib/support/CodeUtils.h>
24 : #include <lib/support/logging/CHIPLogging.h>
25 : #include <protocols/interaction_model/StatusCode.h>
26 :
27 : using chip::Protocols::InteractionModel::Status;
28 :
29 : namespace chip {
30 : namespace app {
31 :
32 48 : CHIP_ERROR CodeDrivenDataModelProvider::Startup(DataModel::InteractionModelContext context)
33 : {
34 48 : ReturnErrorOnFailure(DataModel::Provider::Startup(context));
35 :
36 48 : mInteractionModelContext.emplace(context);
37 :
38 48 : mServerClusterContext.emplace(ServerClusterContext{
39 : .provider = *this,
40 48 : .storage = mPersistentStorageDelegate,
41 48 : .attributeStorage = mAttributePersistenceProvider,
42 48 : .interactionContext = *mInteractionModelContext,
43 : });
44 :
45 : // Start up registered server clusters if one of their associated endpoints is registered.
46 48 : bool had_failure = false;
47 51 : for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
48 : {
49 3 : bool endpointRegistered = false;
50 4 : for (const auto & path : cluster->GetPaths())
51 : {
52 3 : if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
53 : {
54 2 : endpointRegistered = true;
55 2 : break;
56 : }
57 : }
58 :
59 3 : if (endpointRegistered)
60 : {
61 4 : if (cluster->Startup(*mServerClusterContext) != CHIP_NO_ERROR)
62 : {
63 0 : had_failure = true;
64 : }
65 : }
66 : }
67 :
68 48 : if (had_failure)
69 : {
70 0 : return CHIP_ERROR_HAD_FAILURES;
71 : }
72 :
73 48 : return CHIP_NO_ERROR;
74 : }
75 :
76 48 : CHIP_ERROR CodeDrivenDataModelProvider::Shutdown()
77 : {
78 48 : bool had_failure = false;
79 :
80 : // Remove all endpoints. This will trigger Shutdown() on associated clusters.
81 80 : while (mEndpointInterfaceRegistry.begin() != mEndpointInterfaceRegistry.end())
82 : {
83 64 : if (RemoveEndpoint(mEndpointInterfaceRegistry.begin()->GetEndpointEntry().id) != CHIP_NO_ERROR)
84 : {
85 0 : had_failure = true;
86 : }
87 : }
88 :
89 : // Now we're safe to clean up the cluster registry.
90 70 : while (mServerClusterRegistry.AllServerClusterInstances().begin() != mServerClusterRegistry.AllServerClusterInstances().end())
91 : {
92 22 : ServerClusterInterface * clusterToRemove = *mServerClusterRegistry.AllServerClusterInstances().begin();
93 44 : if (mServerClusterRegistry.Unregister(clusterToRemove) != CHIP_NO_ERROR)
94 : {
95 0 : had_failure = true;
96 : }
97 : }
98 :
99 48 : mServerClusterContext.reset();
100 48 : mInteractionModelContext.reset();
101 :
102 48 : if (had_failure)
103 : {
104 0 : return CHIP_ERROR_HAD_FAILURES;
105 : }
106 48 : return CHIP_NO_ERROR;
107 : }
108 :
109 4 : DataModel::ActionReturnStatus CodeDrivenDataModelProvider::ReadAttribute(const DataModel::ReadAttributeRequest & request,
110 : AttributeValueEncoder & encoder)
111 : {
112 4 : ServerClusterInterface * serverCluster = GetServerClusterInterface(request.path);
113 4 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
114 2 : return serverCluster->ReadAttribute(request, encoder);
115 : }
116 :
117 3 : DataModel::ActionReturnStatus CodeDrivenDataModelProvider::WriteAttribute(const DataModel::WriteAttributeRequest & request,
118 : AttributeValueDecoder & decoder)
119 : {
120 3 : ServerClusterInterface * serverCluster = GetServerClusterInterface(request.path);
121 3 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
122 1 : return serverCluster->WriteAttribute(request, decoder);
123 : }
124 :
125 1 : void CodeDrivenDataModelProvider::ListAttributeWriteNotification(const ConcreteAttributePath & path,
126 : DataModel::ListWriteOperation opType, FabricIndex accessingFabric)
127 : {
128 1 : ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
129 1 : VerifyOrReturn(serverCluster != nullptr);
130 1 : serverCluster->ListAttributeWriteNotification(path, opType, accessingFabric);
131 : }
132 :
133 3 : std::optional<DataModel::ActionReturnStatus> CodeDrivenDataModelProvider::InvokeCommand(const DataModel::InvokeRequest & request,
134 : TLV::TLVReader & input_arguments,
135 : CommandHandler * handler)
136 : {
137 3 : ServerClusterInterface * serverCluster = GetServerClusterInterface(request.path);
138 3 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
139 1 : return serverCluster->InvokeCommand(request, input_arguments, handler);
140 : }
141 :
142 3 : CHIP_ERROR CodeDrivenDataModelProvider::Endpoints(ReadOnlyBufferBuilder<DataModel::EndpointEntry> & out)
143 : {
144 : // TODO: Add a size() method to EndpointInterfaceRegistry to avoid iterating twice.
145 3 : size_t count = 0;
146 8 : for (const auto & registration : mEndpointInterfaceRegistry)
147 : {
148 : (void) registration; // Silence unused variable warning
149 5 : count++;
150 : }
151 :
152 3 : ReturnErrorOnFailure(out.EnsureAppendCapacity(count));
153 8 : for (const auto & registration : mEndpointInterfaceRegistry)
154 : {
155 5 : ReturnErrorOnFailure(out.Append(registration.GetEndpointEntry()));
156 : }
157 3 : return CHIP_NO_ERROR;
158 : }
159 :
160 1 : CHIP_ERROR CodeDrivenDataModelProvider::DeviceTypes(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::DeviceTypeEntry> & out)
161 : {
162 1 : EndpointInterface * endpoint = GetEndpointInterface(endpointId);
163 1 : VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
164 1 : return endpoint->DeviceTypes(out);
165 : }
166 :
167 1 : CHIP_ERROR CodeDrivenDataModelProvider::ClientClusters(EndpointId endpointId, ReadOnlyBufferBuilder<ClusterId> & out)
168 : {
169 1 : EndpointInterface * endpoint = GetEndpointInterface(endpointId);
170 1 : VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
171 1 : return endpoint->ClientClusters(out);
172 : }
173 :
174 5 : CHIP_ERROR CodeDrivenDataModelProvider::ServerClusters(EndpointId endpointId,
175 : ReadOnlyBufferBuilder<DataModel::ServerClusterEntry> & out)
176 : {
177 5 : EndpointInterface * endpoint = GetEndpointInterface(endpointId);
178 5 : VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
179 :
180 4 : size_t count = 0;
181 10 : for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
182 : {
183 15 : for (const auto & path : cluster->GetPaths())
184 : {
185 9 : if (path.mEndpointId == endpointId)
186 : {
187 7 : count++;
188 : }
189 : }
190 : }
191 :
192 4 : ReturnErrorOnFailure(out.EnsureAppendCapacity(count));
193 :
194 10 : for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
195 : {
196 15 : for (const auto & path : cluster->GetPaths())
197 : {
198 9 : if (path.mEndpointId == endpointId)
199 : {
200 7 : ReturnErrorOnFailure(
201 : out.Append({ path.mClusterId, cluster->GetDataVersion(path), cluster->GetClusterFlags(path) }));
202 : }
203 : }
204 : }
205 4 : return CHIP_NO_ERROR;
206 : }
207 :
208 1 : CHIP_ERROR CodeDrivenDataModelProvider::GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & out)
209 : {
210 1 : ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
211 1 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
212 1 : return serverCluster->GeneratedCommands(path, out);
213 : }
214 1 : CHIP_ERROR CodeDrivenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path,
215 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & out)
216 : {
217 1 : ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
218 1 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
219 1 : return serverCluster->AcceptedCommands(path, out);
220 : }
221 :
222 1 : CHIP_ERROR CodeDrivenDataModelProvider::Attributes(const ConcreteClusterPath & path,
223 : ReadOnlyBufferBuilder<DataModel::AttributeEntry> & out)
224 : {
225 1 : ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
226 1 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
227 1 : return serverCluster->Attributes(path, out);
228 : }
229 :
230 1 : CHIP_ERROR CodeDrivenDataModelProvider::EventInfo(const ConcreteEventPath & path, DataModel::EventEntry & eventInfo)
231 : {
232 1 : ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
233 1 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
234 1 : return serverCluster->EventInfo(path, eventInfo);
235 : }
236 :
237 : #if CHIP_CONFIG_USE_ENDPOINT_UNIQUE_ID
238 : CHIP_ERROR CodeDrivenDataModelProvider::EndpointUniqueID(EndpointId endpointId, MutableCharSpan & EndpointUniqueId)
239 : {
240 : EndpointInterface * endpoint = GetEndpointInterface(endpointId);
241 : VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
242 : CharSpan uniqueId = endpoint->EndpointUniqueID();
243 : return CopyCharSpanToMutableCharSpan(uniqueId, EndpointUniqueId);
244 : }
245 : #endif
246 :
247 1 : void CodeDrivenDataModelProvider::Temporary_ReportAttributeChanged(const AttributePathParams & path)
248 : {
249 1 : if (!mInteractionModelContext)
250 : {
251 0 : ChipLogError(DataManagement, "Temporary_ReportAttributeChanged called before provider has been started.");
252 0 : return;
253 : }
254 1 : mInteractionModelContext->dataModelChangeListener.MarkDirty(path);
255 : }
256 :
257 42 : CHIP_ERROR CodeDrivenDataModelProvider::AddEndpoint(EndpointInterfaceRegistration & registration)
258 : {
259 42 : VerifyOrReturnError(registration.endpointEntry.id != kInvalidEndpointId, CHIP_ERROR_INVALID_ARGUMENT);
260 :
261 : // If the endpoint ID is already in use, return an error.
262 41 : if (mEndpointInterfaceRegistry.Get(registration.endpointEntry.id) != nullptr)
263 : {
264 1 : return CHIP_ERROR_DUPLICATE_KEY_ID;
265 : }
266 :
267 40 : ReturnErrorOnFailure(mEndpointInterfaceRegistry.Register(registration));
268 :
269 40 : if (mServerClusterContext.has_value())
270 : {
271 : // If the provider has been started, we need to check if any clusters on this new endpoint
272 : // should be started up.
273 60 : for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
274 : {
275 23 : bool clusterIsOnNewEndpoint = false;
276 23 : int registeredEndpointCount = 0;
277 :
278 53 : for (const auto & path : cluster->GetPaths())
279 : {
280 30 : if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
281 : {
282 27 : registeredEndpointCount++;
283 : }
284 30 : if (path.mEndpointId == registration.endpointEntry.id)
285 : {
286 24 : clusterIsOnNewEndpoint = true;
287 : }
288 : }
289 :
290 : // If the cluster is on the endpoint we just added, and this is the *only*
291 : // registered endpoint for this cluster, it's time to start it.
292 23 : if (clusterIsOnNewEndpoint && registeredEndpointCount == 1)
293 : {
294 19 : ReturnErrorOnFailure(cluster->Startup(*mServerClusterContext));
295 : }
296 : }
297 : }
298 :
299 40 : return CHIP_NO_ERROR;
300 : }
301 :
302 41 : CHIP_ERROR CodeDrivenDataModelProvider::RemoveEndpoint(EndpointId endpointId, ClusterShutdownType shutdownType)
303 : {
304 41 : if (mServerClusterContext.has_value())
305 : {
306 : // If the provider has been started, we need to check if any clusters on this endpoint
307 : // need to be shut down because it's their last registered endpoint.
308 66 : for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
309 : {
310 26 : bool clusterIsOnEndpoint = false;
311 26 : int registeredEndpointCount = 0;
312 :
313 59 : for (const auto & path : cluster->GetPaths())
314 : {
315 33 : if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
316 : {
317 29 : registeredEndpointCount++;
318 : }
319 33 : if (path.mEndpointId == endpointId)
320 : {
321 26 : clusterIsOnEndpoint = true;
322 : }
323 : }
324 :
325 26 : if (clusterIsOnEndpoint && registeredEndpointCount == 1)
326 : {
327 : // This is the last registered endpoint for this cluster. Shut it down.
328 21 : cluster->Shutdown(shutdownType);
329 : }
330 : }
331 : }
332 :
333 41 : return mEndpointInterfaceRegistry.Unregister(endpointId);
334 : }
335 :
336 27 : CHIP_ERROR CodeDrivenDataModelProvider::AddCluster(ServerClusterRegistration & entry)
337 : {
338 27 : VerifyOrReturnError(entry.serverClusterInterface != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
339 :
340 27 : if (mServerClusterContext.has_value())
341 : {
342 : // If the provider has been started, prevent non-atomic changes to an endpoint.
343 : // Check if any of the cluster's paths are associated with an already registered endpoint.
344 48 : for (const auto & path : entry.serverClusterInterface->GetPaths())
345 : {
346 26 : if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
347 : {
348 1 : return CHIP_ERROR_INCORRECT_STATE;
349 : }
350 : }
351 : }
352 :
353 26 : return mServerClusterRegistry.Register(entry);
354 : }
355 :
356 4 : CHIP_ERROR CodeDrivenDataModelProvider::RemoveCluster(ServerClusterInterface * cluster, ClusterShutdownType shutdownType)
357 : {
358 4 : VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
359 :
360 4 : if (mServerClusterContext.has_value())
361 : {
362 5 : for (const auto & path : cluster->GetPaths())
363 : {
364 3 : if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
365 : {
366 1 : return CHIP_ERROR_INCORRECT_STATE;
367 : }
368 : }
369 : }
370 :
371 3 : return mServerClusterRegistry.Unregister(cluster, shutdownType);
372 : }
373 :
374 7 : EndpointInterface * CodeDrivenDataModelProvider::GetEndpointInterface(EndpointId endpointId)
375 : {
376 7 : return mEndpointInterfaceRegistry.Get(endpointId);
377 : }
378 :
379 15 : ServerClusterInterface * CodeDrivenDataModelProvider::GetServerClusterInterface(const ConcreteClusterPath & clusterPath)
380 : {
381 15 : return mServerClusterRegistry.Get(clusterPath);
382 : }
383 :
384 : } // namespace app
385 : } // namespace chip
|