Matter SDK Coverage Report
Current view: top level - data-model-providers/codegen - CodegenDataModelProvider.cpp (source / functions) Coverage Total Hit
Test: SHA:02d5c4a2a4954211029a6453d554e1269733873d Lines: 98.3 % 240 236
Test Date: 2025-07-04 07:09:36 Functions: 100.0 % 22 22

            Line data    Source code
       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              : #include <data-model-providers/codegen/CodegenDataModelProvider.h>
      18              : 
      19              : #include <access/AccessControl.h>
      20              : #include <access/Privilege.h>
      21              : #include <app-common/zap-generated/attribute-type.h>
      22              : #include <app/CommandHandlerInterface.h>
      23              : #include <app/CommandHandlerInterfaceRegistry.h>
      24              : #include <app/ConcreteAttributePath.h>
      25              : #include <app/ConcreteClusterPath.h>
      26              : #include <app/ConcreteCommandPath.h>
      27              : #include <app/EventPathParams.h>
      28              : #include <app/GlobalAttributes.h>
      29              : #include <app/RequiredPrivilege.h>
      30              : #include <app/data-model-provider/MetadataTypes.h>
      31              : #include <app/data-model-provider/Provider.h>
      32              : #include <app/persistence/AttributePersistenceProvider.h>
      33              : #include <app/persistence/AttributePersistenceProviderInstance.h>
      34              : #include <app/persistence/DefaultAttributePersistenceProvider.h>
      35              : #include <app/server-cluster/ServerClusterContext.h>
      36              : #include <app/server-cluster/ServerClusterInterface.h>
      37              : #include <app/util/DataModelHandler.h>
      38              : #include <app/util/IMClusterCommandHandler.h>
      39              : #include <app/util/af-types.h>
      40              : #include <app/util/attribute-metadata.h>
      41              : #include <app/util/attribute-storage.h>
      42              : #include <app/util/endpoint-config-api.h>
      43              : #include <data-model-providers/codegen/EmberMetadata.h>
      44              : #include <lib/core/CHIPError.h>
      45              : #include <lib/core/DataModelTypes.h>
      46              : #include <lib/support/CodeUtils.h>
      47              : #include <lib/support/ReadOnlyBuffer.h>
      48              : #include <lib/support/ScopedBuffer.h>
      49              : #include <lib/support/SpanSearchValue.h>
      50              : 
      51              : #include <cstdint>
      52              : #include <optional>
      53              : 
      54              : namespace chip {
      55              : namespace app {
      56              : namespace {
      57              : 
      58          104 : DataModel::AcceptedCommandEntry AcceptedCommandEntryFor(const ConcreteCommandPath & path)
      59              : {
      60          104 :     const CommandId commandId = path.mCommandId;
      61              : 
      62              :     DataModel::AcceptedCommandEntry entry(
      63          104 :         path.mCommandId,
      64          104 :         BitFlags<DataModel::CommandQualityFlags>{}
      65          104 :             .Set(DataModel::CommandQualityFlags::kTimed, CommandNeedsTimedInvoke(path.mClusterId, commandId))
      66          104 :             .Set(DataModel::CommandQualityFlags::kFabricScoped, CommandIsFabricScoped(path.mClusterId, commandId))
      67          104 :             .Set(DataModel::CommandQualityFlags::kLargeMessage, CommandHasLargePayload(path.mClusterId, commandId)),
      68          208 :         RequiredPrivilege::ForInvokeCommand(path));
      69              : 
      70          104 :     return entry;
      71              : }
      72              : 
      73        11470 : DataModel::ServerClusterEntry ServerClusterEntryFrom(EndpointId endpointId, const EmberAfCluster & cluster)
      74              : {
      75        11470 :     DataModel::ServerClusterEntry entry;
      76              : 
      77        11470 :     entry.clusterId = cluster.clusterId;
      78              : 
      79        11470 :     DataVersion * versionPtr = emberAfDataVersionStorage(ConcreteClusterPath(endpointId, cluster.clusterId));
      80        11470 :     if (versionPtr == nullptr)
      81              :     {
      82              : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
      83            0 :         ChipLogError(AppServer, "Failed to get data version for %d/" ChipLogFormatMEI, endpointId,
      84              :                      ChipLogValueMEI(cluster.clusterId));
      85              : #endif
      86            0 :         entry.dataVersion = 0;
      87              :     }
      88              :     else
      89              :     {
      90        11470 :         entry.dataVersion = *versionPtr;
      91              :     }
      92              : 
      93              :     // TODO: set entry flags:
      94              :     //   entry.flags.Set(ClusterQualityFlags::kDiagnosticsData)
      95              : 
      96        11470 :     return entry;
      97              : }
      98              : 
      99        72205 : DataModel::AttributeEntry AttributeEntryFrom(const ConcreteClusterPath & clusterPath, const EmberAfAttributeMetadata & attribute)
     100              : {
     101        72205 :     const ConcreteAttributePath attributePath(clusterPath.mEndpointId, clusterPath.mClusterId, attribute.attributeId);
     102              : 
     103              :     using DataModel::AttributeQualityFlags;
     104              : 
     105              :     DataModel::AttributeEntry entry(
     106        72205 :         attribute.attributeId,
     107        72205 :         BitFlags<DataModel::AttributeQualityFlags>{}
     108        72205 :             .Set(AttributeQualityFlags::kListAttribute, (attribute.attributeType == ZCL_ARRAY_ATTRIBUTE_TYPE))
     109        72205 :             .Set(DataModel::AttributeQualityFlags::kTimed, attribute.MustUseTimedWrite()),
     110        72205 :         RequiredPrivilege::ForReadAttribute(attributePath),
     111       216615 :         attribute.IsReadOnly() ? std::nullopt : std::make_optional(RequiredPrivilege::ForWriteAttribute(attributePath)));
     112              : 
     113              :     // NOTE: we do NOT provide additional info for:
     114              :     //    - IsExternal/IsSingleton/IsAutomaticallyPersisted is not used by IM handling
     115              :     //    - IsSingleton spec defines it for CLUSTERS where as we have it for ATTRIBUTES
     116              :     //    - Several specification flags are not available (reportable, quieter reporting,
     117              :     //      fixed, source attribution)
     118              : 
     119              :     // TODO: Set additional flags:
     120              :     // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricScoped)
     121              :     // entry.flags.Set(DataModel::AttributeQualityFlags::kFabricSensitive)
     122              :     // entry.flags.Set(DataModel::AttributeQualityFlags::kChangesOmitted)
     123        72205 :     return entry;
     124              : }
     125              : 
     126              : DefaultAttributePersistenceProvider gDefaultAttributePersistence;
     127              : 
     128              : } // namespace
     129              : 
     130          159 : CHIP_ERROR CodegenDataModelProvider::Shutdown()
     131              : {
     132          159 :     Reset();
     133          159 :     mRegistry.ClearContext();
     134          159 :     return CHIP_NO_ERROR;
     135              : }
     136              : 
     137          402 : CHIP_ERROR CodegenDataModelProvider::Startup(DataModel::InteractionModelContext context)
     138              : {
     139          402 :     ReturnErrorOnFailure(DataModel::Provider::Startup(context));
     140              : 
     141              :     // Ember NVM requires have a data model provider. attempt to create one if one is not available
     142              :     //
     143              :     // It is not a critical failure to not have one, however if one is not set up, ember NVM operations
     144              :     // will error out with a `persistence not available`.
     145          402 :     if (GetAttributePersistenceProvider() == nullptr)
     146              :     {
     147              : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
     148          399 :         ChipLogProgress(DataManagement, "Ember attribute persistence requires setting up");
     149              : #endif
     150          399 :         if (mPersistentStorageDelegate != nullptr)
     151              :         {
     152            2 :             ReturnErrorOnFailure(gDefaultAttributePersistence.Init(mPersistentStorageDelegate));
     153            2 :             SetAttributePersistenceProvider(&gDefaultAttributePersistence);
     154              : #if CHIP_CONFIG_DATA_MODEL_EXTRA_LOGGING
     155              :         }
     156              :         else
     157              :         {
     158          397 :             ChipLogError(DataManagement, "No storage delegate available, will not set up attribute persistence.");
     159              : #endif
     160              :         }
     161              :     }
     162              : 
     163          402 :     InitDataModelForTesting();
     164              : 
     165          402 :     return mRegistry.SetContext(ServerClusterContext{
     166              :         .provider           = this,
     167          402 :         .storage            = mPersistentStorageDelegate,
     168          402 :         .attributeStorage   = GetAttributePersistenceProvider(),
     169          402 :         .interactionContext = &mContext,
     170          402 :     });
     171              : }
     172              : 
     173            6 : std::optional<DataModel::ActionReturnStatus> CodegenDataModelProvider::InvokeCommand(const DataModel::InvokeRequest & request,
     174              :                                                                                      TLV::TLVReader & input_arguments,
     175              :                                                                                      CommandHandler * handler)
     176              : {
     177            6 :     if (auto * cluster = mRegistry.Get(request.path); cluster != nullptr)
     178              :     {
     179            1 :         return cluster->InvokeCommand(request, input_arguments, handler);
     180              :     }
     181              : 
     182              :     CommandHandlerInterface * handler_interface =
     183            5 :         CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(request.path.mEndpointId, request.path.mClusterId);
     184              : 
     185            5 :     if (handler_interface)
     186              :     {
     187            3 :         CommandHandlerInterface::HandlerContext context(*handler, request.path, input_arguments);
     188            3 :         handler_interface->InvokeCommand(context);
     189              : 
     190              :         // If the command was handled, don't proceed any further and return successfully.
     191            3 :         if (context.mCommandHandled)
     192              :         {
     193            3 :             return std::nullopt;
     194              :         }
     195              :     }
     196              : 
     197              :     // Ember always sets the return in the handler
     198            2 :     DispatchSingleClusterCommand(request.path, input_arguments, handler);
     199            2 :     return std::nullopt;
     200              : }
     201              : 
     202         2942 : CHIP_ERROR CodegenDataModelProvider::Endpoints(ReadOnlyBufferBuilder<DataModel::EndpointEntry> & builder)
     203              : {
     204         2942 :     const uint16_t endpointCount = emberAfEndpointCount();
     205              : 
     206         2942 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(endpointCount));
     207              : 
     208        17226 :     for (uint16_t endpointIndex = 0; endpointIndex < endpointCount; endpointIndex++)
     209              :     {
     210        14284 :         if (!emberAfEndpointIndexIsEnabled(endpointIndex))
     211              :         {
     212         4333 :             continue;
     213              :         }
     214              : 
     215              :         DataModel::EndpointEntry entry;
     216         9951 :         entry.id       = emberAfEndpointFromIndex(endpointIndex);
     217         9951 :         entry.parentId = emberAfParentEndpointFromIndex(endpointIndex);
     218              : 
     219         9951 :         switch (GetCompositionForEndpointIndex(endpointIndex))
     220              :         {
     221         8470 :         case EndpointComposition::kFullFamily:
     222         8470 :             entry.compositionPattern = DataModel::EndpointCompositionPattern::kFullFamily;
     223         8470 :             break;
     224         1481 :         case EndpointComposition::kTree:
     225              :         case EndpointComposition::kInvalid: // should NOT happen, but force compiler to check we validate all versions
     226         1481 :             entry.compositionPattern = DataModel::EndpointCompositionPattern::kTree;
     227         1481 :             break;
     228              :         }
     229         9951 :         ReturnErrorOnFailure(builder.Append(entry));
     230              :     }
     231              : 
     232         2942 :     return CHIP_NO_ERROR;
     233              : }
     234              : 
     235            3 : std::optional<unsigned> CodegenDataModelProvider::TryFindEndpointIndex(EndpointId id) const
     236              : {
     237            3 :     const uint16_t lastEndpointIndex = emberAfEndpointCount();
     238              : 
     239            6 :     if ((mEndpointIterationHint < lastEndpointIndex) && emberAfEndpointIndexIsEnabled(mEndpointIterationHint) &&
     240            3 :         (id == emberAfEndpointFromIndex(mEndpointIterationHint)))
     241              :     {
     242            1 :         return std::make_optional(mEndpointIterationHint);
     243              :     }
     244              : 
     245              :     // Linear search, this may be slow
     246            2 :     uint16_t idx = emberAfIndexFromEndpoint(id);
     247            2 :     if (idx == kEmberInvalidEndpointIndex)
     248              :     {
     249            0 :         return std::nullopt;
     250              :     }
     251              : 
     252            2 :     return std::make_optional<unsigned>(idx);
     253              : }
     254              : 
     255         2241 : CHIP_ERROR CodegenDataModelProvider::EventInfo(const ConcreteEventPath & path, DataModel::EventEntry & eventInfo)
     256              : {
     257         2241 :     if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
     258              :     {
     259            2 :         return cluster->EventInfo(path, eventInfo);
     260              :     }
     261              : 
     262         2239 :     eventInfo.readPrivilege = RequiredPrivilege::ForReadEvent(path);
     263         2239 :     return CHIP_NO_ERROR;
     264              : }
     265              : 
     266         8471 : CHIP_ERROR CodegenDataModelProvider::ServerClusters(EndpointId endpointId,
     267              :                                                     ReadOnlyBufferBuilder<DataModel::ServerClusterEntry> & builder)
     268              : {
     269         8471 :     const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
     270              : 
     271         8471 :     VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
     272         8462 :     VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
     273         8462 :     VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
     274              : 
     275              :     // We build the cluster list by merging two lists:
     276              :     //   - mRegistry items from ServerClusterInterfaces
     277              :     //   - ember metadata clusters
     278              :     //
     279              :     // This is done because `ServerClusterInterface` allows full control for all its metadata,
     280              :     // in particular `data version` and `flags`.
     281              :     //
     282              :     // To allow cluster implementations to be incrementally converted to storing their own data versions,
     283              :     // instead of relying on the out-of-band emberAfDataVersionStorage, first check for clusters that are
     284              :     // using the new data version storage and are registered via ServerClusterInterfaceRegistry, then fill
     285              :     // in the data versions for the rest via the out-of-band mechanism.
     286              : 
     287              :     // assume the clusters on endpoint does not change in between these two loops
     288         8462 :     auto clusters               = mRegistry.ClustersOnEndpoint(endpointId);
     289         8462 :     size_t registryClusterCount = 0;
     290         8463 :     for ([[maybe_unused]] auto _ : clusters)
     291              :     {
     292            1 :         registryClusterCount++;
     293              :     }
     294              : 
     295         8462 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(registryClusterCount));
     296              : 
     297         8462 :     ReadOnlyBufferBuilder<ClusterId> knownClustersBuilder;
     298         8462 :     ReturnErrorOnFailure(knownClustersBuilder.EnsureAppendCapacity(registryClusterCount));
     299         8463 :     for (const auto clusterId : mRegistry.ClustersOnEndpoint(endpointId))
     300              :     {
     301            1 :         ConcreteClusterPath path(endpointId, clusterId);
     302            1 :         ServerClusterInterface * cluster = mRegistry.Get(path);
     303              : 
     304              :         // path MUST be valid: we just got it from iterating our registrations...
     305            1 :         VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_INTERNAL);
     306              : 
     307            1 :         ReturnErrorOnFailure(builder.Append({
     308              :             .clusterId   = path.mClusterId,
     309              :             .dataVersion = cluster->GetDataVersion(path),
     310              :             .flags       = cluster->GetClusterFlags(path),
     311              :         }));
     312            1 :         ReturnErrorOnFailure(knownClustersBuilder.Append(path.mClusterId));
     313              :     }
     314              : 
     315         8462 :     ReadOnlyBuffer<ClusterId> knownClusters = knownClustersBuilder.TakeBuffer();
     316              : 
     317         8462 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ true)));
     318              : 
     319         8462 :     const EmberAfCluster * begin = endpoint->cluster;
     320         8462 :     const EmberAfCluster * end   = endpoint->cluster + endpoint->clusterCount;
     321        21436 :     for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
     322              :     {
     323        12974 :         if (!cluster->IsServer())
     324              :         {
     325         1503 :             continue;
     326              :         }
     327              : 
     328              :         // linear search as this is a somewhat compact number list, so performance is probably not too bad
     329              :         // This results in smaller code than some memory allocation + std::sort + std::binary_search
     330        11471 :         bool found = false;
     331        11472 :         for (ClusterId clusterId : knownClusters)
     332              :         {
     333            2 :             if (clusterId == cluster->clusterId)
     334              :             {
     335            1 :                 found = true;
     336            1 :                 break;
     337              :             }
     338              :         }
     339        11471 :         if (found)
     340              :         {
     341              :             // value already filled from the ServerClusterRegistry. That one has the correct/overriden
     342              :             // flags and data version
     343            1 :             continue;
     344              :         }
     345              : 
     346        11470 :         ReturnErrorOnFailure(builder.Append(ServerClusterEntryFrom(endpointId, *cluster)));
     347              :     }
     348              : 
     349         8462 :     return CHIP_NO_ERROR;
     350         8462 : }
     351              : 
     352        18349 : CHIP_ERROR CodegenDataModelProvider::Attributes(const ConcreteClusterPath & path,
     353              :                                                 ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder)
     354              : {
     355        18349 :     if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
     356              :     {
     357            1 :         return cluster->Attributes(path, builder);
     358              :     }
     359              : 
     360        18348 :     const EmberAfCluster * cluster = FindServerCluster(path);
     361              : 
     362        18348 :     VerifyOrReturnValue(cluster != nullptr, CHIP_ERROR_NOT_FOUND);
     363        17327 :     VerifyOrReturnValue(cluster->attributeCount > 0, CHIP_NO_ERROR);
     364        17327 :     VerifyOrReturnValue(cluster->attributes != nullptr, CHIP_NO_ERROR);
     365              : 
     366              :     // TODO: if ember would encode data in AttributeEntry form, we could reference things directly (shorter code,
     367              :     //       although still allocation overhead due to global attributes not in metadata)
     368              :     //
     369              :     // We have Attributes from ember + global attributes that are NOT in ember metadata.
     370              :     // We have to report them all
     371        17327 :     constexpr size_t kGlobalAttributeNotInMetadataCount = MATTER_ARRAY_SIZE(GlobalAttributesNotInMetadata);
     372              : 
     373        17327 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(cluster->attributeCount + kGlobalAttributeNotInMetadataCount));
     374              : 
     375        17327 :     Span<const EmberAfAttributeMetadata> attributeSpan(cluster->attributes, cluster->attributeCount);
     376              : 
     377        89532 :     for (auto & attribute : attributeSpan)
     378              :     {
     379        72205 :         ReturnErrorOnFailure(builder.Append(AttributeEntryFrom(path, attribute)));
     380              :     }
     381              : 
     382        69308 :     for (auto & attributeId : GlobalAttributesNotInMetadata)
     383              :     {
     384              : 
     385              :         // This "GlobalListEntry" is specific for metadata that ember does not include
     386              :         // in its attribute list metadata.
     387              :         //
     388              :         // By spec these Attribute/AcceptedCommands/GeneratedCommants lists are:
     389              :         //   - lists of elements
     390              :         //   - read-only, with read privilege view
     391              :         //   - fixed value (no such flag exists, so this is not a quality flag we set/track)
     392              :         DataModel::AttributeEntry globalListEntry(attributeId, DataModel::AttributeQualityFlags::kListAttribute,
     393        51981 :                                                   Access::Privilege::kView, std::nullopt);
     394              : 
     395        51981 :         ReturnErrorOnFailure(builder.Append(std::move(globalListEntry)));
     396              :     }
     397              : 
     398        17327 :     return CHIP_NO_ERROR;
     399              : }
     400              : 
     401            4 : CHIP_ERROR CodegenDataModelProvider::ClientClusters(EndpointId endpointId, ReadOnlyBufferBuilder<ClusterId> & builder)
     402              : {
     403            4 :     const EmberAfEndpointType * endpoint = emberAfFindEndpointType(endpointId);
     404              : 
     405            4 :     VerifyOrReturnValue(endpoint != nullptr, CHIP_ERROR_NOT_FOUND);
     406            2 :     VerifyOrReturnValue(endpoint->clusterCount > 0, CHIP_NO_ERROR);
     407            2 :     VerifyOrReturnValue(endpoint->cluster != nullptr, CHIP_NO_ERROR);
     408              : 
     409            2 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(emberAfClusterCountForEndpointType(endpoint, /* server = */ false)));
     410              : 
     411            2 :     const EmberAfCluster * begin = endpoint->cluster;
     412            2 :     const EmberAfCluster * end   = endpoint->cluster + endpoint->clusterCount;
     413           10 :     for (const EmberAfCluster * cluster = begin; cluster != end; cluster++)
     414              :     {
     415            8 :         if (!cluster->IsClient())
     416              :         {
     417            4 :             continue;
     418              :         }
     419            4 :         ReturnErrorOnFailure(builder.Append(cluster->clusterId));
     420              :     }
     421              : 
     422            2 :     return CHIP_NO_ERROR;
     423              : }
     424              : 
     425        19155 : const EmberAfCluster * CodegenDataModelProvider::FindServerCluster(const ConcreteClusterPath & path)
     426              : {
     427        36937 :     if (mPreviouslyFoundCluster.has_value() && (mPreviouslyFoundCluster->path == path) &&
     428        17782 :         (mEmberMetadataStructureGeneration == emberAfMetadataStructureGeneration()))
     429              : 
     430              :     {
     431        17670 :         return mPreviouslyFoundCluster->cluster;
     432              :     }
     433              : 
     434         1485 :     const EmberAfCluster * cluster = emberAfFindServerCluster(path.mEndpointId, path.mClusterId);
     435         1485 :     if (cluster != nullptr)
     436              :     {
     437          451 :         mPreviouslyFoundCluster           = std::make_optional<ClusterReference>(path, cluster);
     438          451 :         mEmberMetadataStructureGeneration = emberAfMetadataStructureGeneration();
     439              :     }
     440         1485 :     return cluster;
     441              : }
     442              : 
     443          431 : CHIP_ERROR CodegenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path,
     444              :                                                       ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder)
     445              : {
     446          431 :     if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
     447              :     {
     448            1 :         return cluster->AcceptedCommands(path, builder);
     449              :     }
     450              : 
     451              :     // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
     452              :     // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
     453              :     // it claims to support.
     454          430 :     const EmberAfCluster * serverCluster = FindServerCluster(path);
     455          430 :     VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
     456              : 
     457              :     CommandHandlerInterface * interface =
     458          423 :         CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
     459          423 :     if (interface != nullptr)
     460              :     {
     461           16 :         size_t commandCount = 0;
     462              : 
     463           16 :         CHIP_ERROR err = interface->EnumerateAcceptedCommands(
     464              :             path,
     465            6 :             [](CommandId id, void * context) -> Loop {
     466            6 :                 *reinterpret_cast<size_t *>(context) += 1;
     467            6 :                 return Loop::Continue;
     468              :             },
     469              :             reinterpret_cast<void *>(&commandCount));
     470              : 
     471           16 :         if (err == CHIP_NO_ERROR)
     472              :         {
     473              :             using EnumerationData = struct
     474              :             {
     475              :                 ConcreteCommandPath commandPath;
     476              :                 ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> * acceptedCommandList;
     477              :                 CHIP_ERROR processingError;
     478              :             };
     479              : 
     480            8 :             EnumerationData enumerationData;
     481            8 :             enumerationData.commandPath         = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
     482            8 :             enumerationData.processingError     = CHIP_NO_ERROR;
     483            8 :             enumerationData.acceptedCommandList = &builder;
     484              : 
     485            8 :             ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
     486              : 
     487           14 :             ReturnErrorOnFailure(interface->EnumerateAcceptedCommands(
     488              :                 path,
     489              :                 [](CommandId commandId, void * context) -> Loop {
     490              :                     auto input                    = reinterpret_cast<EnumerationData *>(context);
     491              :                     input->commandPath.mCommandId = commandId;
     492              :                     CHIP_ERROR appendError        = input->acceptedCommandList->Append(AcceptedCommandEntryFor(input->commandPath));
     493              :                     if (appendError != CHIP_NO_ERROR)
     494              :                     {
     495              :                         input->processingError = appendError;
     496              :                         return Loop::Break;
     497              :                     }
     498              :                     return Loop::Continue;
     499              :                 },
     500              :                 reinterpret_cast<void *>(&enumerationData)));
     501            8 :             ReturnErrorOnFailure(enumerationData.processingError);
     502              : 
     503              :             // the two invocations MUST return the same sizes.
     504            8 :             VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL);
     505            8 :             return CHIP_NO_ERROR;
     506              :         }
     507            8 :         VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
     508              :     }
     509              : 
     510          415 :     VerifyOrReturnError(serverCluster->acceptedCommandList != nullptr, CHIP_NO_ERROR);
     511              : 
     512           30 :     const chip::CommandId * endOfList = serverCluster->acceptedCommandList;
     513          128 :     while (*endOfList != kInvalidCommandId)
     514              :     {
     515           98 :         endOfList++;
     516              :     }
     517           30 :     const auto commandCount = static_cast<size_t>(endOfList - serverCluster->acceptedCommandList);
     518              : 
     519              :     // TODO: if ember would store command entries, we could simplify this code to use static data
     520           30 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
     521              : 
     522           30 :     ConcreteCommandPath commandPath = ConcreteCommandPath(path.mEndpointId, path.mClusterId, kInvalidCommandId);
     523          128 :     for (const chip::CommandId * p = serverCluster->acceptedCommandList; p != endOfList; p++)
     524              :     {
     525           98 :         commandPath.mCommandId = *p;
     526           98 :         ReturnErrorOnFailure(builder.Append(AcceptedCommandEntryFor(commandPath)));
     527              :     }
     528              : 
     529           30 :     return CHIP_NO_ERROR;
     530              : }
     531              : 
     532          378 : CHIP_ERROR CodegenDataModelProvider::GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & builder)
     533              : {
     534          378 :     if (auto * cluster = mRegistry.Get(path); cluster != nullptr)
     535              :     {
     536            1 :         return cluster->GeneratedCommands(path, builder);
     537              :     }
     538              : 
     539              :     // Some CommandHandlerInterface instances are registered of ALL endpoints, so make sure first that
     540              :     // the cluster actually exists on this endpoint before asking the CommandHandlerInterface what commands
     541              :     // it claims to support.
     542          377 :     const EmberAfCluster * serverCluster = FindServerCluster(path);
     543          377 :     VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_NOT_FOUND);
     544              : 
     545              :     CommandHandlerInterface * interface =
     546          371 :         CommandHandlerInterfaceRegistry::Instance().GetCommandHandler(path.mEndpointId, path.mClusterId);
     547          371 :     if (interface != nullptr)
     548              :     {
     549            4 :         size_t commandCount = 0;
     550              : 
     551            4 :         CHIP_ERROR err = interface->EnumerateGeneratedCommands(
     552              :             path,
     553            1 :             [](CommandId id, void * context) -> Loop {
     554            1 :                 *reinterpret_cast<size_t *>(context) += 1;
     555            1 :                 return Loop::Continue;
     556              :             },
     557              :             reinterpret_cast<void *>(&commandCount));
     558              : 
     559            4 :         if (err == CHIP_NO_ERROR)
     560              :         {
     561            2 :             ReturnErrorOnFailure(builder.EnsureAppendCapacity(commandCount));
     562              : 
     563              :             using EnumerationData = struct
     564              :             {
     565              :                 ReadOnlyBufferBuilder<CommandId> * generatedCommandList;
     566              :                 CHIP_ERROR processingError;
     567              :             };
     568              :             EnumerationData enumerationData;
     569            2 :             enumerationData.processingError      = CHIP_NO_ERROR;
     570            2 :             enumerationData.generatedCommandList = &builder;
     571              : 
     572            3 :             ReturnErrorOnFailure(interface->EnumerateGeneratedCommands(
     573              :                 path,
     574              :                 [](CommandId id, void * context) -> Loop {
     575              :                     auto input = reinterpret_cast<EnumerationData *>(context);
     576              : 
     577              :                     CHIP_ERROR appendError = input->generatedCommandList->Append(id);
     578              :                     if (appendError != CHIP_NO_ERROR)
     579              :                     {
     580              :                         input->processingError = appendError;
     581              :                         return Loop::Break;
     582              :                     }
     583              :                     return Loop::Continue;
     584              :                 },
     585              :                 reinterpret_cast<void *>(&enumerationData)));
     586            2 :             ReturnErrorOnFailure(enumerationData.processingError);
     587              : 
     588              :             // the two invocations MUST return the same sizes.
     589            2 :             VerifyOrReturnError(builder.Size() == commandCount, CHIP_ERROR_INTERNAL);
     590            2 :             return CHIP_NO_ERROR;
     591              :         }
     592            2 :         VerifyOrReturnError(err == CHIP_ERROR_NOT_IMPLEMENTED, err);
     593              :     }
     594              : 
     595          369 :     VerifyOrReturnError(serverCluster->generatedCommandList != nullptr, CHIP_NO_ERROR);
     596              : 
     597            2 :     const chip::CommandId * endOfList = serverCluster->generatedCommandList;
     598            6 :     while (*endOfList != kInvalidCommandId)
     599              :     {
     600            4 :         endOfList++;
     601              :     }
     602            2 :     const auto commandCount = static_cast<size_t>(endOfList - serverCluster->generatedCommandList);
     603            2 :     return builder.ReferenceExisting({ serverCluster->generatedCommandList, commandCount });
     604              : }
     605              : 
     606          395 : void CodegenDataModelProvider::InitDataModelForTesting()
     607              : {
     608              :     // Call the Ember-specific InitDataModelHandler
     609          395 :     InitDataModelHandler();
     610          395 : }
     611              : 
     612            3 : CHIP_ERROR CodegenDataModelProvider::DeviceTypes(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::DeviceTypeEntry> & builder)
     613              : {
     614            3 :     std::optional<unsigned> endpoint_index = TryFindEndpointIndex(endpointId);
     615            3 :     if (!endpoint_index.has_value())
     616              :     {
     617            0 :         return {};
     618              :     }
     619              : 
     620            3 :     CHIP_ERROR err = CHIP_NO_ERROR;
     621              : 
     622            3 :     return builder.ReferenceExisting(emberAfDeviceTypeListFromEndpointIndex(*endpoint_index, err));
     623              : }
     624              : 
     625            2 : CHIP_ERROR CodegenDataModelProvider::SemanticTags(EndpointId endpointId, ReadOnlyBufferBuilder<SemanticTag> & builder)
     626              : {
     627            2 :     DataModel::Provider::SemanticTag semanticTag;
     628            2 :     size_t count = 0;
     629              : 
     630            5 :     while (GetSemanticTagForEndpointAtIndex(endpointId, count, semanticTag) == CHIP_NO_ERROR)
     631              :     {
     632            3 :         count++;
     633              :     }
     634              : 
     635            2 :     ReturnErrorOnFailure(builder.EnsureAppendCapacity(count));
     636              : 
     637            5 :     for (size_t idx = 0; idx < count; idx++)
     638              :     {
     639            3 :         ReturnErrorOnFailure(GetSemanticTagForEndpointAtIndex(endpointId, idx, semanticTag));
     640            3 :         ReturnErrorOnFailure(builder.Append(semanticTag));
     641              :     }
     642              : 
     643            2 :     return CHIP_NO_ERROR;
     644              : }
     645              : #if CHIP_CONFIG_USE_ENDPOINT_UNIQUE_ID
     646              : CHIP_ERROR CodegenDataModelProvider::EndpointUniqueID(EndpointId endpointId, MutableCharSpan & epUniqueId)
     647              : {
     648              :     char buffer[Clusters::Descriptor::Attributes::EndpointUniqueID::TypeInfo::MaxLength()] = { 0 };
     649              :     MutableCharSpan epUniqueIdSpan(buffer);
     650              :     emberAfGetEndpointUniqueIdForEndPoint(endpointId, epUniqueIdSpan);
     651              : 
     652              :     memcpy(epUniqueId.data(), epUniqueIdSpan.data(), epUniqueIdSpan.size());
     653              :     return CHIP_NO_ERROR;
     654              : }
     655              : #endif
     656              : 
     657              : } // namespace app
     658              : } // namespace chip
        

Generated by: LCOV version 2.0-1