Matter SDK Coverage Report
Current view: top level - data-model-providers/codedriven - CodeDrivenDataModelProvider.cpp (source / functions) Coverage Total Hit
Test: SHA:2a48c1efeab1c0f76f3adb3a0940b0f7de706453 Lines: 95.3 % 149 142
Test Date: 2026-01-31 08:14:20 Functions: 100.0 % 21 21

            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
        

Generated by: LCOV version 2.0-1