Matter SDK Coverage Report
Current view: top level - app/util - attribute-storage.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 42.3 % 560 237
Test Date: 2025-01-17 19:00:11 Functions: 50.0 % 74 37

            Line data    Source code
       1              : /**
       2              :  *
       3              :  *    Copyright (c) 2020-2023 Project CHIP Authors
       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              : #include <app/util/attribute-storage.h>
      19              : 
      20              : #include <app/util/attribute-storage-detail.h>
      21              : 
      22              : #include <app/AttributeAccessInterfaceRegistry.h>
      23              : #include <app/CommandHandlerInterfaceRegistry.h>
      24              : #include <app/InteractionModelEngine.h>
      25              : #include <app/reporting/reporting.h>
      26              : #include <app/util/config.h>
      27              : #include <app/util/ember-strings.h>
      28              : #include <app/util/endpoint-config-api.h>
      29              : #include <app/util/generic-callbacks.h>
      30              : #include <app/util/persistence/AttributePersistenceProvider.h>
      31              : #include <lib/core/CHIPConfig.h>
      32              : #include <lib/support/CodeUtils.h>
      33              : #include <lib/support/logging/CHIPLogging.h>
      34              : #include <platform/LockTracker.h>
      35              : #include <protocols/interaction_model/StatusCode.h>
      36              : 
      37              : using chip::Protocols::InteractionModel::Status;
      38              : 
      39              : // Attribute storage depends on knowing the current layout/setup of attributes
      40              : // and corresponding callbacks. Specifically:
      41              : //   - zap-generated/callback.h is needed because endpoint_config will call the
      42              : //     corresponding callbacks (via GENERATED_FUNCTION_ARRAYS) and the include
      43              : //     for it is:
      44              : //     util/config.h -> zap-generated/endpoint_config.h
      45              : #include <app-common/zap-generated/callback.h>
      46              : 
      47              : using namespace chip;
      48              : using namespace chip::app;
      49              : 
      50              : //------------------------------------------------------------------------------
      51              : // Globals
      52              : // This is not declared CONST in order to handle dynamic endpoint information
      53              : // retrieved from tokens.
      54              : EmberAfDefinedEndpoint emAfEndpoints[MAX_ENDPOINT_COUNT];
      55              : 
      56              : #if (ATTRIBUTE_MAX_SIZE == 0)
      57              : #define ACTUAL_ATTRIBUTE_SIZE 1
      58              : #else
      59              : #define ACTUAL_ATTRIBUTE_SIZE ATTRIBUTE_MAX_SIZE
      60              : #endif
      61              : 
      62              : uint8_t attributeData[ACTUAL_ATTRIBUTE_SIZE];
      63              : 
      64              : // ----- internal-only methods, not part of the external API -----
      65              : 
      66              : // Loads the attributes from built-in default and storage.
      67              : static void emAfLoadAttributeDefaults(EndpointId endpoint, Optional<ClusterId> = NullOptional);
      68              : 
      69              : static bool emAfMatchCluster(const EmberAfCluster * cluster, const EmberAfAttributeSearchRecord * attRecord);
      70              : static bool emAfMatchAttribute(const EmberAfCluster * cluster, const EmberAfAttributeMetadata * am,
      71              :                                const EmberAfAttributeSearchRecord * attRecord);
      72              : 
      73              : // If server == true, returns the number of server clusters,
      74              : // otherwise number of client clusters on the endpoint at the given index.
      75              : static uint8_t emberAfClusterCountForEndpointType(const EmberAfEndpointType * endpointType, bool server);
      76              : 
      77              : // If server == true, returns the number of server clusters,
      78              : // otherwise number of client clusters on the endpoint at the given index.
      79              : static uint8_t emberAfClusterCountByIndex(uint16_t endpointIndex, bool server);
      80              : 
      81              : // Check whether there is an endpoint defined with the given endpoint id that is
      82              : // enabled.
      83              : static bool emberAfEndpointIsEnabled(EndpointId endpoint);
      84              : 
      85              : namespace {
      86              : 
      87              : #if (!defined(ATTRIBUTE_SINGLETONS_SIZE)) || (ATTRIBUTE_SINGLETONS_SIZE == 0)
      88              : #define ACTUAL_SINGLETONS_SIZE 1
      89              : #else
      90              : #define ACTUAL_SINGLETONS_SIZE ATTRIBUTE_SINGLETONS_SIZE
      91              : #endif
      92              : uint8_t singletonAttributeData[ACTUAL_SINGLETONS_SIZE];
      93              : 
      94              : uint16_t emberEndpointCount = 0;
      95              : 
      96              : /// Determines a incremental unique index for ember
      97              : /// metadata that is increased whenever a structural change is made to the
      98              : /// ember metadata (e.g. changing dynamic endpoints or enabling/disabling endpoints)
      99              : unsigned emberMetadataStructureGeneration = 0;
     100              : 
     101              : // If we have attributes that are more than 4 bytes, then
     102              : // we need this data block for the defaults
     103              : #if (defined(GENERATED_DEFAULTS) && GENERATED_DEFAULTS_COUNT)
     104              : constexpr const uint8_t generatedDefaults[] = GENERATED_DEFAULTS;
     105              : #define ZAP_LONG_DEFAULTS_INDEX(index)                                                                                             \
     106              :     {                                                                                                                              \
     107              :         &generatedDefaults[index]                                                                                                  \
     108              :     }
     109              : #endif // GENERATED_DEFAULTS
     110              : 
     111              : #if (defined(GENERATED_MIN_MAX_DEFAULTS) && GENERATED_MIN_MAX_DEFAULT_COUNT)
     112              : constexpr const EmberAfAttributeMinMaxValue minMaxDefaults[] = GENERATED_MIN_MAX_DEFAULTS;
     113              : #define ZAP_MIN_MAX_DEFAULTS_INDEX(index)                                                                                          \
     114              :     {                                                                                                                              \
     115              :         &minMaxDefaults[index]                                                                                                     \
     116              :     }
     117              : #endif // GENERATED_MIN_MAX_DEFAULTS
     118              : 
     119              : #ifdef GENERATED_FUNCTION_ARRAYS
     120              : GENERATED_FUNCTION_ARRAYS
     121              : #endif
     122              : 
     123              : #ifdef GENERATED_COMMANDS
     124              : constexpr const CommandId generatedCommands[] = GENERATED_COMMANDS;
     125              : #define ZAP_GENERATED_COMMANDS_INDEX(index) (&generatedCommands[index])
     126              : #endif // GENERATED_COMMANDS
     127              : 
     128              : #if (defined(GENERATED_EVENTS) && (GENERATED_EVENT_COUNT > 0))
     129              : constexpr const EventId generatedEvents[] = GENERATED_EVENTS;
     130              : #define ZAP_GENERATED_EVENTS_INDEX(index) (&generatedEvents[index])
     131              : #endif // GENERATED_EVENTS
     132              : 
     133              : constexpr const EmberAfAttributeMetadata generatedAttributes[] = GENERATED_ATTRIBUTES;
     134              : #define ZAP_ATTRIBUTE_INDEX(index) (&generatedAttributes[index])
     135              : 
     136              : #ifdef GENERATED_CLUSTERS
     137              : constexpr const EmberAfCluster generatedClusters[] = GENERATED_CLUSTERS;
     138              : #define ZAP_CLUSTER_INDEX(index) (&generatedClusters[index])
     139              : #endif
     140              : 
     141              : #if FIXED_ENDPOINT_COUNT > 0
     142              : constexpr const EmberAfEndpointType generatedEmberAfEndpointTypes[] = GENERATED_ENDPOINT_TYPES;
     143              : constexpr const EmberAfDeviceType fixedDeviceTypeList[]             = FIXED_DEVICE_TYPES;
     144              : 
     145              : // Not const, because these need to mutate.
     146              : DataVersion fixedEndpointDataVersions[ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT];
     147              : #endif // FIXED_ENDPOINT_COUNT > 0
     148              : 
     149            0 : bool emberAfIsThisDataTypeAListType(EmberAfAttributeType dataType)
     150              : {
     151            0 :     return dataType == ZCL_ARRAY_ATTRIBUTE_TYPE;
     152              : }
     153              : 
     154         6203 : uint16_t findIndexFromEndpoint(EndpointId endpoint, bool ignoreDisabledEndpoints)
     155              : {
     156         6203 :     if (endpoint == kInvalidEndpointId)
     157              :     {
     158            0 :         return kEmberInvalidEndpointIndex;
     159              :     }
     160              : 
     161              :     uint16_t epi;
     162        12632 :     for (epi = 0; epi < emberAfEndpointCount(); epi++)
     163              :     {
     164        18737 :         if (emAfEndpoints[epi].endpoint == endpoint &&
     165         6154 :             (!ignoreDisabledEndpoints || emAfEndpoints[epi].bitmask.Has(EmberAfEndpointOptions::isEnabled)))
     166              :         {
     167         6154 :             return epi;
     168              :         }
     169              :     }
     170           49 :     return kEmberInvalidEndpointIndex;
     171              : }
     172              : 
     173              : // Returns the index of a given endpoint.  Considers disabled endpoints.
     174            0 : uint16_t emberAfIndexFromEndpointIncludingDisabledEndpoints(EndpointId endpoint)
     175              : {
     176            0 :     return findIndexFromEndpoint(endpoint, false /* ignoreDisabledEndpoints */);
     177              : }
     178              : 
     179              : } // anonymous namespace
     180              : 
     181              : // Initial configuration
     182           15 : void emberAfEndpointConfigure()
     183              : {
     184              :     uint16_t ep;
     185              : 
     186              :     static_assert(FIXED_ENDPOINT_COUNT <= std::numeric_limits<decltype(ep)>::max(),
     187              :                   "FIXED_ENDPOINT_COUNT must not exceed the size of the endpoint data type");
     188              : 
     189           15 :     emberEndpointCount = FIXED_ENDPOINT_COUNT;
     190              : 
     191              : #if FIXED_ENDPOINT_COUNT > 0
     192              : 
     193           15 :     constexpr uint16_t fixedEndpoints[]             = FIXED_ENDPOINT_ARRAY;
     194           15 :     constexpr uint16_t fixedDeviceTypeListLengths[] = FIXED_DEVICE_TYPE_LENGTHS;
     195           15 :     constexpr uint16_t fixedDeviceTypeListOffsets[] = FIXED_DEVICE_TYPE_OFFSETS;
     196           15 :     constexpr uint8_t fixedEmberAfEndpointTypes[]   = FIXED_ENDPOINT_TYPES;
     197           15 :     constexpr EndpointId fixedParentEndpoints[]     = FIXED_PARENT_ENDPOINTS;
     198              : 
     199              : #if ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT > 0
     200              :     // Initialize our data version storage.  If
     201              :     // ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT == 0, gcc complains about a memset
     202              :     // with size equal to number of elements without multiplication by element
     203              :     // size, because the sizeof() is also 0 in that case...
     204              :     if (Crypto::DRBG_get_bytes(reinterpret_cast<uint8_t *>(fixedEndpointDataVersions), sizeof(fixedEndpointDataVersions)) !=
     205              :         CHIP_NO_ERROR)
     206              :     {
     207              :         // Now what?  At least 0-init it.
     208              :         memset(fixedEndpointDataVersions, 0, sizeof(fixedEndpointDataVersions));
     209              :     }
     210              : #endif // ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT > 0
     211              : 
     212           15 :     DataVersion * currentDataVersions = fixedEndpointDataVersions;
     213           30 :     for (ep = 0; ep < FIXED_ENDPOINT_COUNT; ep++)
     214              :     {
     215           15 :         emAfEndpoints[ep].endpoint = fixedEndpoints[ep];
     216           15 :         emAfEndpoints[ep].deviceTypeList =
     217           15 :             Span<const EmberAfDeviceType>(&fixedDeviceTypeList[fixedDeviceTypeListOffsets[ep]], fixedDeviceTypeListLengths[ep]);
     218           15 :         emAfEndpoints[ep].endpointType     = &generatedEmberAfEndpointTypes[fixedEmberAfEndpointTypes[ep]];
     219           15 :         emAfEndpoints[ep].dataVersions     = currentDataVersions;
     220           15 :         emAfEndpoints[ep].parentEndpointId = fixedParentEndpoints[ep];
     221              : 
     222           15 :         emAfEndpoints[ep].bitmask.Set(EmberAfEndpointOptions::isEnabled);
     223           15 :         emAfEndpoints[ep].bitmask.Set(EmberAfEndpointOptions::isFlatComposition);
     224              : 
     225              :         // Increment currentDataVersions by 1 (slot) for every server cluster
     226              :         // this endpoint has.
     227           15 :         currentDataVersions += emberAfClusterCountByIndex(ep, /* server = */ true);
     228              :     }
     229              : 
     230              : #endif // FIXED_ENDPOINT_COUNT > 0
     231              : 
     232              : #if CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT
     233              :     if (MAX_ENDPOINT_COUNT > FIXED_ENDPOINT_COUNT)
     234              :     {
     235              :         //
     236              :         // Reset instances tracking dynamic endpoints to safe defaults.
     237              :         //
     238           75 :         for (ep = FIXED_ENDPOINT_COUNT; ep < MAX_ENDPOINT_COUNT; ep++)
     239              :         {
     240           60 :             emAfEndpoints[ep] = EmberAfDefinedEndpoint();
     241              :         }
     242              :     }
     243              : #endif
     244           15 : }
     245              : 
     246           23 : void emberAfSetDynamicEndpointCount(uint16_t dynamicEndpointCount)
     247              : {
     248           23 :     emberEndpointCount = static_cast<uint16_t>(FIXED_ENDPOINT_COUNT + dynamicEndpointCount);
     249           23 : }
     250              : 
     251            0 : uint16_t emberAfGetDynamicIndexFromEndpoint(EndpointId id)
     252              : {
     253            0 :     if (id == kInvalidEndpointId)
     254              :     {
     255            0 :         return kEmberInvalidEndpointIndex;
     256              :     }
     257              : 
     258              :     uint16_t index;
     259            0 :     for (index = FIXED_ENDPOINT_COUNT; index < MAX_ENDPOINT_COUNT; index++)
     260              :     {
     261            0 :         if (emAfEndpoints[index].endpoint == id)
     262              :         {
     263            0 :             return static_cast<uint16_t>(index - FIXED_ENDPOINT_COUNT);
     264              :         }
     265              :     }
     266            0 :     return kEmberInvalidEndpointIndex;
     267              : }
     268              : 
     269           23 : CHIP_ERROR emberAfSetDynamicEndpoint(uint16_t index, EndpointId id, const EmberAfEndpointType * ep,
     270              :                                      const Span<DataVersion> & dataVersionStorage, Span<const EmberAfDeviceType> deviceTypeList,
     271              :                                      EndpointId parentEndpointId)
     272              : {
     273           23 :     auto realIndex = index + FIXED_ENDPOINT_COUNT;
     274              : 
     275           23 :     if (realIndex >= MAX_ENDPOINT_COUNT)
     276              :     {
     277            0 :         return CHIP_ERROR_NO_MEMORY;
     278              :     }
     279           23 :     if (id == kInvalidEndpointId)
     280              :     {
     281            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     282              :     }
     283              : 
     284           23 :     auto serverClusterCount = emberAfClusterCountForEndpointType(ep, /* server = */ true);
     285           23 :     if (dataVersionStorage.size() < serverClusterCount)
     286              :     {
     287            0 :         return CHIP_ERROR_NO_MEMORY;
     288              :     }
     289              : 
     290           23 :     index = static_cast<uint16_t>(realIndex);
     291          115 :     for (uint16_t i = FIXED_ENDPOINT_COUNT; i < MAX_ENDPOINT_COUNT; i++)
     292              :     {
     293           92 :         if (emAfEndpoints[i].endpoint == id)
     294              :         {
     295            0 :             return CHIP_ERROR_ENDPOINT_EXISTS;
     296              :         }
     297              :     }
     298              : 
     299           23 :     emAfEndpoints[index].endpoint       = id;
     300           23 :     emAfEndpoints[index].deviceTypeList = deviceTypeList;
     301           23 :     emAfEndpoints[index].endpointType   = ep;
     302           23 :     emAfEndpoints[index].dataVersions   = dataVersionStorage.data();
     303              :     // Start the endpoint off as disabled.
     304           23 :     emAfEndpoints[index].bitmask.Clear(EmberAfEndpointOptions::isEnabled);
     305           23 :     emAfEndpoints[index].parentEndpointId = parentEndpointId;
     306              : 
     307           23 :     emberAfSetDynamicEndpointCount(MAX_ENDPOINT_COUNT - FIXED_ENDPOINT_COUNT);
     308              : 
     309              :     // Initialize the data versions.
     310           23 :     size_t dataSize = sizeof(DataVersion) * serverClusterCount;
     311           23 :     if (dataSize != 0)
     312              :     {
     313           23 :         if (Crypto::DRBG_get_bytes(reinterpret_cast<uint8_t *>(dataVersionStorage.data()), dataSize) != CHIP_NO_ERROR)
     314              :         {
     315              :             // Now what?  At least 0-init it.
     316            0 :             memset(dataVersionStorage.data(), 0, dataSize);
     317              :         }
     318              :     }
     319              : 
     320              :     // Now enable the endpoint.
     321           23 :     emberAfEndpointEnableDisable(id, true);
     322              : 
     323           23 :     emberMetadataStructureGeneration++;
     324           23 :     return CHIP_NO_ERROR;
     325              : }
     326              : 
     327           22 : EndpointId emberAfClearDynamicEndpoint(uint16_t index)
     328              : {
     329           22 :     EndpointId ep = 0;
     330              : 
     331           22 :     index = static_cast<uint16_t>(index + FIXED_ENDPOINT_COUNT);
     332              : 
     333           44 :     if ((index < MAX_ENDPOINT_COUNT) && (emAfEndpoints[index].endpoint != kInvalidEndpointId) &&
     334           22 :         (emberAfEndpointIndexIsEnabled(index)))
     335              :     {
     336           22 :         ep = emAfEndpoints[index].endpoint;
     337           22 :         emberAfEndpointEnableDisable(ep, false);
     338           22 :         emAfEndpoints[index].endpoint = kInvalidEndpointId;
     339              :     }
     340              : 
     341           22 :     emberMetadataStructureGeneration++;
     342           22 :     return ep;
     343              : }
     344              : 
     345         8405 : uint16_t emberAfFixedEndpointCount()
     346              : {
     347         8405 :     return FIXED_ENDPOINT_COUNT;
     348              : }
     349              : 
     350        31821 : uint16_t emberAfEndpointCount()
     351              : {
     352        31821 :     return emberEndpointCount;
     353              : }
     354              : 
     355         4336 : bool emberAfEndpointIndexIsEnabled(uint16_t index)
     356              : {
     357         4336 :     return (emAfEndpoints[index].bitmask.Has(EmberAfEndpointOptions::isEnabled));
     358              : }
     359              : 
     360              : // This function is used to call the per-cluster attribute changed callback
     361            0 : void emAfClusterAttributeChangedCallback(const ConcreteAttributePath & attributePath)
     362              : {
     363            0 :     const EmberAfCluster * cluster = emberAfFindServerCluster(attributePath.mEndpointId, attributePath.mClusterId);
     364            0 :     if (cluster != nullptr)
     365              :     {
     366            0 :         EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_ATTRIBUTE_CHANGED_FUNCTION);
     367            0 :         if (f != nullptr)
     368              :         {
     369            0 :             ((EmberAfClusterAttributeChangedCallback) f)(attributePath);
     370              :         }
     371              :     }
     372            0 : }
     373              : 
     374              : // This function is used to call the per-cluster pre-attribute changed callback
     375            0 : Status emAfClusterPreAttributeChangedCallback(const ConcreteAttributePath & attributePath, EmberAfAttributeType attributeType,
     376              :                                               uint16_t size, uint8_t * value)
     377              : {
     378            0 :     const EmberAfCluster * cluster = emberAfFindServerCluster(attributePath.mEndpointId, attributePath.mClusterId);
     379            0 :     if (cluster == nullptr)
     380              :     {
     381            0 :         if (!emberAfEndpointIsEnabled(attributePath.mEndpointId))
     382              :         {
     383            0 :             return Status::UnsupportedEndpoint;
     384              :         }
     385            0 :         return Status::UnsupportedCluster;
     386              :     }
     387              : 
     388            0 :     Status status = Status::Success;
     389              :     // Casting and calling a function pointer on the same line results in ignoring the return
     390              :     // of the call on gcc-arm-none-eabi-9-2019-q4-major
     391            0 :     EmberAfClusterPreAttributeChangedCallback f = (EmberAfClusterPreAttributeChangedCallback) (emberAfFindClusterFunction(
     392              :         cluster, CLUSTER_MASK_PRE_ATTRIBUTE_CHANGED_FUNCTION));
     393            0 :     if (f != nullptr)
     394              :     {
     395            0 :         status = f(attributePath, attributeType, size, value);
     396              :     }
     397            0 :     return status;
     398              : }
     399              : 
     400           39 : static void initializeEndpoint(EmberAfDefinedEndpoint * definedEndpoint)
     401              : {
     402              :     uint8_t clusterIndex;
     403           39 :     const EmberAfEndpointType * epType = definedEndpoint->endpointType;
     404         1674 :     for (clusterIndex = 0; clusterIndex < epType->clusterCount; clusterIndex++)
     405              :     {
     406         1635 :         const EmberAfCluster * cluster = &(epType->cluster[clusterIndex]);
     407              :         EmberAfGenericClusterFunction f;
     408         1635 :         emberAfClusterInitCallback(definedEndpoint->endpoint, cluster->clusterId);
     409         1635 :         f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_INIT_FUNCTION);
     410         1635 :         if (f != nullptr)
     411              :         {
     412            0 :             ((EmberAfInitFunction) f)(definedEndpoint->endpoint);
     413              :         }
     414              :     }
     415           39 : }
     416              : 
     417           23 : static void shutdownEndpoint(EmberAfDefinedEndpoint * definedEndpoint)
     418              : {
     419              :     // Call shutdown callbacks from clusters, mainly for canceling pending timers
     420              :     uint8_t clusterIndex;
     421           23 :     const EmberAfEndpointType * epType = definedEndpoint->endpointType;
     422           52 :     for (clusterIndex = 0; clusterIndex < epType->clusterCount; clusterIndex++)
     423              :     {
     424           29 :         const EmberAfCluster * cluster  = &(epType->cluster[clusterIndex]);
     425           29 :         EmberAfGenericClusterFunction f = emberAfFindClusterFunction(cluster, CLUSTER_MASK_SHUTDOWN_FUNCTION);
     426           29 :         if (f != nullptr)
     427              :         {
     428            0 :             ((EmberAfShutdownFunction) f)(definedEndpoint->endpoint);
     429              :         }
     430              :     }
     431              : 
     432           23 :     CommandHandlerInterfaceRegistry::Instance().UnregisterAllCommandHandlersForEndpoint(definedEndpoint->endpoint);
     433           23 :     AttributeAccessInterfaceRegistry::Instance().UnregisterAllForEndpoint(definedEndpoint->endpoint);
     434           23 : }
     435              : 
     436              : // Calls the init functions.
     437           15 : void emAfCallInits()
     438              : {
     439              :     uint16_t index;
     440           30 :     for (index = 0; index < emberAfEndpointCount(); index++)
     441              :     {
     442           15 :         if (emberAfEndpointIndexIsEnabled(index))
     443              :         {
     444           15 :             initializeEndpoint(&(emAfEndpoints[index]));
     445              :         }
     446              :     }
     447           15 : }
     448              : 
     449              : // Returns the pointer to metadata, or null if it is not found
     450         4190 : const EmberAfAttributeMetadata * emberAfLocateAttributeMetadata(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId)
     451              : {
     452         4190 :     const EmberAfAttributeMetadata * metadata = nullptr;
     453              :     EmberAfAttributeSearchRecord record;
     454         4190 :     record.endpoint    = endpoint;
     455         4190 :     record.clusterId   = clusterId;
     456         4190 :     record.attributeId = attributeId;
     457         4190 :     emAfReadOrWriteAttribute(&record, &metadata,
     458              :                              nullptr, // buffer
     459              :                              0,       // buffer size
     460              :                              false);  // write?
     461         4190 :     return metadata;
     462              : }
     463              : 
     464            0 : static uint8_t * singletonAttributeLocation(const EmberAfAttributeMetadata * am)
     465              : {
     466            0 :     const EmberAfAttributeMetadata * m = &(generatedAttributes[0]);
     467            0 :     uint16_t index                     = 0;
     468            0 :     while (m < am)
     469              :     {
     470            0 :         if (m->IsSingleton() && !m->IsExternal())
     471              :         {
     472            0 :             index = static_cast<uint16_t>(index + m->size);
     473              :         }
     474            0 :         m++;
     475              :     }
     476            0 :     return (uint8_t *) (singletonAttributeData + index);
     477              : }
     478              : 
     479              : // This function does mem copy, but smartly, which means that if the type is a
     480              : // string, it will copy as much as it can.
     481              : // If src == NULL, then this method will set memory to zeroes
     482              : // See documentation for emAfReadOrWriteAttribute for the semantics of
     483              : // readLength when reading and writing.
     484            0 : static Status typeSensitiveMemCopy(ClusterId clusterId, uint8_t * dest, uint8_t * src, const EmberAfAttributeMetadata * am,
     485              :                                    bool write, uint16_t readLength)
     486              : {
     487            0 :     EmberAfAttributeType attributeType = am->attributeType;
     488              :     // readLength == 0 for a read indicates that we should just trust that the
     489              :     // caller has enough space for an attribute...
     490            0 :     bool ignoreReadLength = write || (readLength == 0);
     491            0 :     uint16_t bufferSize   = ignoreReadLength ? am->size : readLength;
     492              : 
     493            0 :     if (emberAfIsStringAttributeType(attributeType))
     494              :     {
     495            0 :         if (bufferSize < 1)
     496              :         {
     497            0 :             return Status::ResourceExhausted;
     498              :         }
     499            0 :         emberAfCopyString(dest, src, bufferSize - 1);
     500              :     }
     501            0 :     else if (emberAfIsLongStringAttributeType(attributeType))
     502              :     {
     503            0 :         if (bufferSize < 2)
     504              :         {
     505            0 :             return Status::ResourceExhausted;
     506              :         }
     507            0 :         emberAfCopyLongString(dest, src, bufferSize - 2);
     508              :     }
     509            0 :     else if (emberAfIsThisDataTypeAListType(attributeType))
     510              :     {
     511            0 :         if (bufferSize < 2)
     512              :         {
     513            0 :             return Status::ResourceExhausted;
     514              :         }
     515              : 
     516              :         // Just copy the length.
     517            0 :         memmove(dest, src, 2);
     518              :     }
     519              :     else
     520              :     {
     521            0 :         if (!ignoreReadLength && readLength < am->size)
     522              :         {
     523            0 :             return Status::ResourceExhausted;
     524              :         }
     525            0 :         if (src == nullptr)
     526              :         {
     527            0 :             memset(dest, 0, am->size);
     528              :         }
     529              :         else
     530              :         {
     531            0 :             memmove(dest, src, am->size);
     532              :         }
     533              :     }
     534            0 :     return Status::Success;
     535              : }
     536              : 
     537              : /**
     538              :  * @brief Matches a cluster based on cluster id and direction.
     539              :  *
     540              :  *   This function assumes that the passed cluster's endpoint already
     541              :  *   matches the endpoint of the EmberAfAttributeSearchRecord.
     542              :  *
     543              :  * Clusters match if:
     544              :  *   1. Cluster ids match AND
     545              :  *   2. Cluster is a server cluster (because there are no client attributes).
     546              :  */
     547         4190 : bool emAfMatchCluster(const EmberAfCluster * cluster, const EmberAfAttributeSearchRecord * attRecord)
     548              : {
     549         4190 :     return (cluster->clusterId == attRecord->clusterId && (cluster->mask & CLUSTER_MASK_SERVER));
     550              : }
     551              : 
     552              : /**
     553              :  * @brief Matches an attribute based on attribute id.
     554              :  *   This function assumes that the passed cluster already matches the
     555              :  *   clusterId and direction of the passed EmberAfAttributeSearchRecord.
     556              :  *
     557              :  * Attributes match if attr ids match.
     558              :  */
     559         7263 : bool emAfMatchAttribute(const EmberAfCluster * cluster, const EmberAfAttributeMetadata * am,
     560              :                         const EmberAfAttributeSearchRecord * attRecord)
     561              : {
     562         7263 :     return (am->attributeId == attRecord->attributeId);
     563              : }
     564              : 
     565              : // When reading non-string attributes, this function returns an error when destination
     566              : // buffer isn't large enough to accommodate the attribute type.  For strings, the
     567              : // function will copy at most readLength bytes.  This means the resulting string
     568              : // may be truncated.  The length byte(s) in the resulting string will reflect
     569              : // any truncation.  If readLength is zero, we are working with backwards-
     570              : // compatibility wrapper functions and we just cross our fingers and hope for
     571              : // the best.
     572              : //
     573              : // When writing attributes, readLength is ignored.  For non-string attributes,
     574              : // this function assumes the source buffer is the same size as the attribute
     575              : // type.  For strings, the function will copy as many bytes as will fit in the
     576              : // attribute.  This means the resulting string may be truncated.  The length
     577              : // byte(s) in the resulting string will reflect any truncated.
     578         4190 : Status emAfReadOrWriteAttribute(const EmberAfAttributeSearchRecord * attRecord, const EmberAfAttributeMetadata ** metadata,
     579              :                                 uint8_t * buffer, uint16_t readLength, bool write)
     580              : {
     581         4190 :     assertChipStackLockedByCurrentThread();
     582              : 
     583         4190 :     uint16_t attributeOffsetIndex = 0;
     584              : 
     585         8405 :     for (uint16_t ep = 0; ep < emberAfEndpointCount(); ep++)
     586              :     {
     587              :         // Is this a dynamic endpoint?
     588         8405 :         bool isDynamicEndpoint = (ep >= emberAfFixedEndpointCount());
     589              : 
     590         8405 :         if (emAfEndpoints[ep].endpoint == attRecord->endpoint)
     591              :         {
     592         4190 :             const EmberAfEndpointType * endpointType = emAfEndpoints[ep].endpointType;
     593              :             uint8_t clusterIndex;
     594         4190 :             if (!emberAfEndpointIndexIsEnabled(ep))
     595              :             {
     596            0 :                 continue;
     597              :             }
     598         4190 :             for (clusterIndex = 0; clusterIndex < endpointType->clusterCount; clusterIndex++)
     599              :             {
     600         4190 :                 const EmberAfCluster * cluster = &(endpointType->cluster[clusterIndex]);
     601         4190 :                 if (emAfMatchCluster(cluster, attRecord))
     602              :                 { // Got the cluster
     603              :                     uint16_t attrIndex;
     604         7263 :                     for (attrIndex = 0; attrIndex < cluster->attributeCount; attrIndex++)
     605              :                     {
     606         7263 :                         const EmberAfAttributeMetadata * am = &(cluster->attributes[attrIndex]);
     607         7263 :                         if (emAfMatchAttribute(cluster, am, attRecord))
     608              :                         { // Got the attribute
     609              :                             // If passed metadata location is not null, populate
     610         4190 :                             if (metadata != nullptr)
     611              :                             {
     612         4190 :                                 *metadata = am;
     613              :                             }
     614              : 
     615              :                             {
     616              :                                 uint8_t * attributeLocation =
     617         4190 :                                     (am->mask & ATTRIBUTE_MASK_SINGLETON ? singletonAttributeLocation(am)
     618         4190 :                                                                          : attributeData + attributeOffsetIndex);
     619              :                                 uint8_t *src, *dst;
     620         4190 :                                 if (write)
     621              :                                 {
     622            0 :                                     src = buffer;
     623            0 :                                     dst = attributeLocation;
     624            0 :                                     if (!emberAfAttributeWriteAccessCallback(attRecord->endpoint, attRecord->clusterId,
     625            0 :                                                                              am->attributeId))
     626              :                                     {
     627            0 :                                         return Status::UnsupportedAccess;
     628              :                                     }
     629              :                                 }
     630              :                                 else
     631              :                                 {
     632         4190 :                                     if (buffer == nullptr)
     633              :                                     {
     634         4190 :                                         return Status::Success;
     635              :                                     }
     636              : 
     637            0 :                                     src = attributeLocation;
     638            0 :                                     dst = buffer;
     639            0 :                                     if (!emberAfAttributeReadAccessCallback(attRecord->endpoint, attRecord->clusterId,
     640            0 :                                                                             am->attributeId))
     641              :                                     {
     642            0 :                                         return Status::UnsupportedAccess;
     643              :                                     }
     644              :                                 }
     645              : 
     646              :                                 // Is the attribute externally stored?
     647            0 :                                 if (am->mask & ATTRIBUTE_MASK_EXTERNAL_STORAGE)
     648              :                                 {
     649            0 :                                     return (write ? emberAfExternalAttributeWriteCallback(attRecord->endpoint, attRecord->clusterId,
     650              :                                                                                           am, buffer)
     651            0 :                                                   : emberAfExternalAttributeReadCallback(attRecord->endpoint, attRecord->clusterId,
     652            0 :                                                                                          am, buffer, emberAfAttributeSize(am)));
     653              :                                 }
     654              : 
     655              :                                 // Internal storage is only supported for fixed endpoints
     656            0 :                                 if (!isDynamicEndpoint)
     657              :                                 {
     658            0 :                                     return typeSensitiveMemCopy(attRecord->clusterId, dst, src, am, write, readLength);
     659              :                                 }
     660              : 
     661            0 :                                 return Status::Failure;
     662              :                             }
     663              :                         }
     664              :                         else
     665              :                         { // Not the attribute we are looking for
     666              :                             // Increase the index if attribute is not externally stored
     667         3073 :                             if (!(am->mask & ATTRIBUTE_MASK_EXTERNAL_STORAGE) && !(am->mask & ATTRIBUTE_MASK_SINGLETON))
     668              :                             {
     669            0 :                                 attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + emberAfAttributeSize(am));
     670              :                             }
     671              :                         }
     672              :                     }
     673              : 
     674              :                     // Attribute is not in the cluster.
     675            0 :                     return Status::UnsupportedAttribute;
     676              :                 }
     677              : 
     678              :                 // Not the cluster we are looking for
     679            0 :                 attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + cluster->clusterSize);
     680              :             }
     681              : 
     682              :             // Cluster is not in the endpoint.
     683            0 :             return Status::UnsupportedCluster;
     684              :         }
     685              : 
     686              :         // Not the endpoint we are looking for
     687              :         // Dynamic endpoints are external and don't factor into storage size
     688         4215 :         if (!isDynamicEndpoint)
     689              :         {
     690         4190 :             attributeOffsetIndex = static_cast<uint16_t>(attributeOffsetIndex + emAfEndpoints[ep].endpointType->endpointSize);
     691              :         }
     692              :     }
     693            0 :     return Status::UnsupportedEndpoint; // Sorry, endpoint was not found.
     694              : }
     695              : 
     696           43 : const EmberAfEndpointType * emberAfFindEndpointType(EndpointId endpointId)
     697              : {
     698           43 :     uint16_t ep = emberAfIndexFromEndpoint(endpointId);
     699           43 :     if (ep == kEmberInvalidEndpointIndex)
     700              :     {
     701            0 :         return nullptr;
     702              :     }
     703           43 :     return emAfEndpoints[ep].endpointType;
     704              : }
     705              : 
     706         6064 : const EmberAfCluster * emberAfFindClusterInType(const EmberAfEndpointType * endpointType, ClusterId clusterId,
     707              :                                                 EmberAfClusterMask mask, uint8_t * index)
     708              : {
     709              :     uint8_t i;
     710         6064 :     uint8_t scopedIndex = 0;
     711              : 
     712         6064 :     for (i = 0; i < endpointType->clusterCount; i++)
     713              :     {
     714         6064 :         const EmberAfCluster * cluster = &(endpointType->cluster[i]);
     715              : 
     716         6064 :         if (mask == 0 || ((cluster->mask & mask) != 0))
     717              :         {
     718         6064 :             if (cluster->clusterId == clusterId)
     719              :             {
     720         6064 :                 if (index)
     721              :                 {
     722         5320 :                     *index = scopedIndex;
     723              :                 }
     724              : 
     725         6064 :                 return cluster;
     726              :             }
     727              : 
     728            0 :             scopedIndex++;
     729              :         }
     730              :     }
     731              : 
     732            0 :     return nullptr;
     733              : }
     734              : 
     735         5320 : uint8_t emberAfClusterIndex(EndpointId endpoint, ClusterId clusterId, EmberAfClusterMask mask)
     736              : {
     737        10673 :     for (uint16_t ep = 0; ep < emberAfEndpointCount(); ep++)
     738              :     {
     739              :         // Check the endpoint id first, because that way we avoid examining the
     740              :         // endpoint type for endpoints that are not actually defined.
     741        10673 :         if (emAfEndpoints[ep].endpoint == endpoint)
     742              :         {
     743         5320 :             const EmberAfEndpointType * endpointType = emAfEndpoints[ep].endpointType;
     744         5320 :             uint8_t index                            = 0xFF;
     745         5320 :             if (emberAfFindClusterInType(endpointType, clusterId, mask, &index) != nullptr)
     746              :             {
     747         5320 :                 return index;
     748              :             }
     749              :         }
     750              :     }
     751            0 :     return 0xFF;
     752              : }
     753              : 
     754              : // Returns whether the given endpoint has the server of the given cluster on it.
     755            0 : bool emberAfContainsServer(EndpointId endpoint, ClusterId clusterId)
     756              : {
     757            0 :     return (emberAfFindServerCluster(endpoint, clusterId) != nullptr);
     758              : }
     759              : 
     760              : // Returns whether the given endpoint has the client of the given cluster on it.
     761            0 : bool emberAfContainsClient(EndpointId endpoint, ClusterId clusterId)
     762              : {
     763            0 :     uint16_t ep = emberAfIndexFromEndpoint(endpoint);
     764            0 :     if (ep == kEmberInvalidEndpointIndex)
     765              :     {
     766            0 :         return false;
     767              :     }
     768              : 
     769            0 :     return (emberAfFindClusterInType(emAfEndpoints[ep].endpointType, clusterId, CLUSTER_MASK_CLIENT) != nullptr);
     770              : }
     771              : 
     772              : // This will find the first server that has the clusterId given from the index of endpoint.
     773            0 : bool emberAfContainsServerFromIndex(uint16_t index, ClusterId clusterId)
     774              : {
     775            0 :     if (index == kEmberInvalidEndpointIndex)
     776              :     {
     777            0 :         return false;
     778              :     }
     779              : 
     780            0 :     return emberAfFindClusterInType(emAfEndpoints[index].endpointType, clusterId, CLUSTER_MASK_SERVER);
     781              : }
     782              : 
     783              : namespace chip {
     784              : namespace app {
     785              : 
     786            0 : EnabledEndpointsWithServerCluster::EnabledEndpointsWithServerCluster(ClusterId clusterId) :
     787            0 :     mEndpointCount(emberAfEndpointCount()), mClusterId(clusterId)
     788              : {
     789            0 :     EnsureMatchingEndpoint();
     790            0 : }
     791              : 
     792            0 : EndpointId EnabledEndpointsWithServerCluster::operator*() const
     793              : {
     794            0 :     return emberAfEndpointFromIndex(mEndpointIndex);
     795              : }
     796              : 
     797            0 : EnabledEndpointsWithServerCluster & EnabledEndpointsWithServerCluster::operator++()
     798              : {
     799            0 :     ++mEndpointIndex;
     800            0 :     EnsureMatchingEndpoint();
     801            0 :     return *this;
     802              : }
     803              : 
     804            0 : void EnabledEndpointsWithServerCluster::EnsureMatchingEndpoint()
     805              : {
     806            0 :     for (; mEndpointIndex < mEndpointCount; ++mEndpointIndex)
     807              :     {
     808            0 :         if (!emberAfEndpointIndexIsEnabled(mEndpointIndex))
     809              :         {
     810            0 :             continue;
     811              :         }
     812              : 
     813            0 :         if (emberAfContainsServerFromIndex(mEndpointIndex, mClusterId))
     814              :         {
     815            0 :             break;
     816              :         }
     817              :     }
     818            0 : }
     819              : 
     820              : } // namespace app
     821              : } // namespace chip
     822              : 
     823              : // Finds the cluster that matches endpoint, clusterId, direction.
     824          746 : const EmberAfCluster * emberAfFindServerCluster(EndpointId endpoint, ClusterId clusterId)
     825              : {
     826          746 :     uint16_t ep = emberAfIndexFromEndpoint(endpoint);
     827          746 :     if (ep == kEmberInvalidEndpointIndex)
     828              :     {
     829            2 :         return nullptr;
     830              :     }
     831              : 
     832          744 :     return emberAfFindClusterInType(emAfEndpoints[ep].endpointType, clusterId, CLUSTER_MASK_SERVER);
     833              : }
     834              : 
     835              : // Returns cluster within the endpoint; Does not ignore disabled endpoints
     836            0 : const EmberAfCluster * emberAfFindClusterIncludingDisabledEndpoints(EndpointId endpoint, ClusterId clusterId,
     837              :                                                                     EmberAfClusterMask mask)
     838              : {
     839            0 :     uint16_t ep = emberAfIndexFromEndpointIncludingDisabledEndpoints(endpoint);
     840            0 :     if (ep < MAX_ENDPOINT_COUNT)
     841              :     {
     842            0 :         return emberAfFindClusterInType(emAfEndpoints[ep].endpointType, clusterId, mask);
     843              :     }
     844            0 :     return nullptr;
     845              : }
     846              : 
     847            0 : uint16_t emberAfGetClusterServerEndpointIndex(EndpointId endpoint, ClusterId cluster, uint16_t fixedClusterServerEndpointCount)
     848              : {
     849            0 :     VerifyOrDie(fixedClusterServerEndpointCount <= FIXED_ENDPOINT_COUNT);
     850            0 :     uint16_t epIndex = findIndexFromEndpoint(endpoint, true /*ignoreDisabledEndpoints*/);
     851              : 
     852              :     // Endpoint must be configured and enabled
     853            0 :     if (epIndex == kEmberInvalidEndpointIndex)
     854              :     {
     855            0 :         return kEmberInvalidEndpointIndex;
     856              :     }
     857              : 
     858            0 :     if (emberAfFindClusterInType(emAfEndpoints[epIndex].endpointType, cluster, CLUSTER_MASK_SERVER) == nullptr)
     859              :     {
     860              :         // The provided endpoint does not contain the given cluster server.
     861            0 :         return kEmberInvalidEndpointIndex;
     862              :     }
     863              : 
     864            0 :     if (epIndex < FIXED_ENDPOINT_COUNT)
     865              :     {
     866              :         // This endpoint is a fixed one.
     867              :         // Return the index of this endpoint in the list of fixed endpoints that support the given cluster.
     868            0 :         uint16_t adjustedEndpointIndex = 0;
     869            0 :         for (uint16_t i = 0; i < epIndex; i++)
     870              :         {
     871              :             // Increase adjustedEndpointIndex for every endpoint containing the cluster server
     872              :             // before our endpoint of interest
     873            0 :             if (emAfEndpoints[i].endpoint != kInvalidEndpointId &&
     874            0 :                 (emberAfFindClusterInType(emAfEndpoints[i].endpointType, cluster, CLUSTER_MASK_SERVER) != nullptr))
     875              :             {
     876            0 :                 adjustedEndpointIndex++;
     877              :             }
     878              :         }
     879              : 
     880              :         // If this asserts, the provided fixedClusterServerEndpointCount doesn't match the app data model.
     881            0 :         VerifyOrDie(adjustedEndpointIndex < fixedClusterServerEndpointCount);
     882            0 :         epIndex = adjustedEndpointIndex;
     883              :     }
     884              :     else
     885              :     {
     886              :         // This is a dynamic endpoint.
     887              :         // Its index is just its index in the dynamic endpoint list, offset by fixedClusterServerEndpointCount.
     888            0 :         epIndex = static_cast<uint16_t>(fixedClusterServerEndpointCount + (epIndex - FIXED_ENDPOINT_COUNT));
     889              :     }
     890              : 
     891            0 :     return epIndex;
     892              : }
     893              : 
     894            0 : bool emberAfEndpointIsEnabled(EndpointId endpoint)
     895              : {
     896            0 :     uint16_t index = findIndexFromEndpoint(endpoint, false /* ignoreDisabledEndpoints */);
     897              : 
     898            0 :     if (kEmberInvalidEndpointIndex == index)
     899              :     {
     900            0 :         return false;
     901              :     }
     902              : 
     903            0 :     return emberAfEndpointIndexIsEnabled(index);
     904              : }
     905              : 
     906           47 : bool emberAfEndpointEnableDisable(EndpointId endpoint, bool enable)
     907              : {
     908           47 :     uint16_t index = findIndexFromEndpoint(endpoint, false /* ignoreDisabledEndpoints */);
     909              :     bool currentlyEnabled;
     910              : 
     911           47 :     if (kEmberInvalidEndpointIndex == index)
     912              :     {
     913            0 :         return false;
     914              :     }
     915              : 
     916           47 :     currentlyEnabled = emAfEndpoints[index].bitmask.Has(EmberAfEndpointOptions::isEnabled);
     917              : 
     918           47 :     if (enable)
     919              :     {
     920           24 :         emAfEndpoints[index].bitmask.Set(EmberAfEndpointOptions::isEnabled);
     921              :     }
     922              : 
     923           47 :     if (currentlyEnabled != enable)
     924              :     {
     925           47 :         if (enable)
     926              :         {
     927           24 :             initializeEndpoint(&(emAfEndpoints[index]));
     928           24 :             emberAfEndpointChanged(endpoint, emberAfGlobalInteractionModelAttributesChangedListener());
     929              :         }
     930              :         else
     931              :         {
     932           23 :             shutdownEndpoint(&(emAfEndpoints[index]));
     933           23 :             emAfEndpoints[index].bitmask.Clear(EmberAfEndpointOptions::isEnabled);
     934              :         }
     935              : 
     936           47 :         EndpointId parentEndpointId = emberAfParentEndpointFromIndex(index);
     937           47 :         while (parentEndpointId != kInvalidEndpointId)
     938              :         {
     939            0 :             emberAfAttributeChanged(parentEndpointId, Clusters::Descriptor::Id, Clusters::Descriptor::Attributes::PartsList::Id,
     940              :                                     emberAfGlobalInteractionModelAttributesChangedListener());
     941            0 :             uint16_t parentIndex = emberAfIndexFromEndpoint(parentEndpointId);
     942            0 :             if (parentIndex == kEmberInvalidEndpointIndex)
     943              :             {
     944              :                 // Something has gone wrong.
     945            0 :                 break;
     946              :             }
     947            0 :             parentEndpointId = emberAfParentEndpointFromIndex(parentIndex);
     948              :         }
     949              : 
     950           47 :         emberAfAttributeChanged(/* endpoint = */ 0, Clusters::Descriptor::Id, Clusters::Descriptor::Attributes::PartsList::Id,
     951              :                                 emberAfGlobalInteractionModelAttributesChangedListener());
     952              :     }
     953              : 
     954           47 :     emberMetadataStructureGeneration++;
     955           47 :     return true;
     956              : }
     957              : 
     958         7906 : unsigned emberAfMetadataStructureGeneration()
     959              : {
     960         7906 :     return emberMetadataStructureGeneration;
     961              : }
     962              : 
     963              : // Returns the index of a given endpoint.  Does not consider disabled endpoints.
     964         6156 : uint16_t emberAfIndexFromEndpoint(EndpointId endpoint)
     965              : {
     966         6156 :     return findIndexFromEndpoint(endpoint, true /* ignoreDisabledEndpoints */);
     967              : }
     968              : 
     969           56 : EndpointId emberAfEndpointFromIndex(uint16_t index)
     970              : {
     971           56 :     return emAfEndpoints[index].endpoint;
     972              : }
     973              : 
     974           77 : EndpointId emberAfParentEndpointFromIndex(uint16_t index)
     975              : {
     976           77 :     return emAfEndpoints[index].parentEndpointId;
     977              : }
     978              : 
     979              : // If server == true, returns the number of server clusters,
     980              : // otherwise number of client clusters on this endpoint
     981            0 : uint8_t emberAfClusterCount(EndpointId endpoint, bool server)
     982              : {
     983            0 :     uint16_t index = emberAfIndexFromEndpoint(endpoint);
     984            0 :     if (index == kEmberInvalidEndpointIndex)
     985              :     {
     986            0 :         return 0;
     987              :     }
     988              : 
     989            0 :     return emberAfClusterCountByIndex(index, server);
     990              : }
     991              : 
     992           15 : uint8_t emberAfClusterCountByIndex(uint16_t endpointIndex, bool server)
     993              : {
     994           15 :     const EmberAfDefinedEndpoint * de = &(emAfEndpoints[endpointIndex]);
     995           15 :     if (de->endpointType == nullptr)
     996              :     {
     997            0 :         return 0;
     998              :     }
     999              : 
    1000           15 :     return emberAfClusterCountForEndpointType(de->endpointType, server);
    1001              : }
    1002              : 
    1003           38 : uint8_t emberAfClusterCountForEndpointType(const EmberAfEndpointType * type, bool server)
    1004              : {
    1005           38 :     const EmberAfClusterMask cluster_mask = server ? CLUSTER_MASK_SERVER : CLUSTER_MASK_CLIENT;
    1006              : 
    1007           38 :     return static_cast<uint8_t>(std::count_if(type->cluster, type->cluster + type->clusterCount,
    1008         1672 :                                               [=](const EmberAfCluster & cluster) { return (cluster.mask & cluster_mask) != 0; }));
    1009              : }
    1010              : 
    1011            0 : uint8_t emberAfGetClusterCountForEndpoint(EndpointId endpoint)
    1012              : {
    1013            0 :     uint16_t index = emberAfIndexFromEndpoint(endpoint);
    1014            0 :     if (index == kEmberInvalidEndpointIndex)
    1015              :     {
    1016            0 :         return 0;
    1017              :     }
    1018            0 :     return emAfEndpoints[index].endpointType->clusterCount;
    1019              : }
    1020              : 
    1021            0 : Span<const EmberAfDeviceType> emberAfDeviceTypeListFromEndpoint(EndpointId endpoint, CHIP_ERROR & err)
    1022              : {
    1023            0 :     return emberAfDeviceTypeListFromEndpointIndex(emberAfIndexFromEndpoint(endpoint), err);
    1024              : }
    1025              : 
    1026            0 : chip::Span<const EmberAfDeviceType> emberAfDeviceTypeListFromEndpointIndex(unsigned endpointIndex, CHIP_ERROR & err)
    1027              : {
    1028            0 :     if (endpointIndex == 0xFFFF)
    1029              :     {
    1030            0 :         err = CHIP_ERROR_INVALID_ARGUMENT;
    1031            0 :         return Span<const EmberAfDeviceType>();
    1032              :     }
    1033              : 
    1034            0 :     err = CHIP_NO_ERROR;
    1035            0 :     return emAfEndpoints[endpointIndex].deviceTypeList;
    1036              : }
    1037              : 
    1038            0 : CHIP_ERROR GetSemanticTagForEndpointAtIndex(EndpointId endpoint, size_t index,
    1039              :                                             Clusters::Descriptor::Structs::SemanticTagStruct::Type & tag)
    1040              : {
    1041            0 :     uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
    1042              : 
    1043            0 :     if (endpointIndex == 0xFFFF || index >= emAfEndpoints[endpointIndex].tagList.size())
    1044              :     {
    1045            0 :         return CHIP_ERROR_NOT_FOUND;
    1046              :     }
    1047            0 :     tag = emAfEndpoints[endpointIndex].tagList[index];
    1048            0 :     return CHIP_NO_ERROR;
    1049              : }
    1050              : 
    1051            0 : CHIP_ERROR emberAfSetDeviceTypeList(EndpointId endpoint, Span<const EmberAfDeviceType> deviceTypeList)
    1052              : {
    1053            0 :     uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
    1054            0 :     if (endpointIndex == 0xFFFF)
    1055              :     {
    1056            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
    1057              :     }
    1058              : 
    1059            0 :     emAfEndpoints[endpointIndex].deviceTypeList = deviceTypeList;
    1060            0 :     return CHIP_NO_ERROR;
    1061              : }
    1062              : 
    1063            0 : CHIP_ERROR SetTagList(EndpointId endpoint, Span<const Clusters::Descriptor::Structs::SemanticTagStruct::Type> tagList)
    1064              : {
    1065            0 :     uint16_t endpointIndex = emberAfIndexFromEndpoint(endpoint);
    1066            0 :     if (endpointIndex == 0xFFFF)
    1067              :     {
    1068            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
    1069              :     }
    1070              : 
    1071            0 :     emAfEndpoints[endpointIndex].tagList = tagList;
    1072            0 :     return CHIP_NO_ERROR;
    1073              : }
    1074              : 
    1075              : // Returns the cluster of Nth server or client cluster,
    1076              : // depending on server toggle.
    1077            0 : const EmberAfCluster * emberAfGetNthCluster(EndpointId endpoint, uint8_t n, bool server)
    1078              : {
    1079            0 :     uint16_t index = emberAfIndexFromEndpoint(endpoint);
    1080            0 :     if (index == kEmberInvalidEndpointIndex)
    1081              :     {
    1082            0 :         return nullptr;
    1083              :     }
    1084              : 
    1085            0 :     const EmberAfEndpointType * endpointType = emAfEndpoints[index].endpointType;
    1086            0 :     const EmberAfClusterMask cluster_mask    = server ? CLUSTER_MASK_SERVER : CLUSTER_MASK_CLIENT;
    1087            0 :     const uint8_t clusterCount               = endpointType->clusterCount;
    1088              : 
    1089            0 :     uint8_t c = 0;
    1090            0 :     for (uint8_t i = 0; i < clusterCount; i++)
    1091              :     {
    1092            0 :         const EmberAfCluster * cluster = &(endpointType->cluster[i]);
    1093              : 
    1094            0 :         if ((cluster->mask & cluster_mask) == 0)
    1095              :         {
    1096            0 :             continue;
    1097              :         }
    1098              : 
    1099            0 :         if (c == n)
    1100              :         {
    1101            0 :             return cluster;
    1102              :         }
    1103              : 
    1104            0 :         c++;
    1105              :     }
    1106            0 :     return nullptr;
    1107              : }
    1108              : 
    1109              : // Returns the cluster id of Nth server or client cluster,
    1110              : // depending on server toggle.
    1111              : // Returns Optional<ClusterId>::Missing() if cluster does not exist.
    1112            0 : Optional<ClusterId> emberAfGetNthClusterId(EndpointId endpoint, uint8_t n, bool server)
    1113              : {
    1114            0 :     const EmberAfCluster * cluster = emberAfGetNthCluster(endpoint, n, server);
    1115            0 :     if (cluster == nullptr)
    1116              :     {
    1117            0 :         return Optional<ClusterId>::Missing();
    1118              :     }
    1119            0 :     return Optional<ClusterId>(cluster->clusterId);
    1120              : }
    1121              : 
    1122              : // Returns number of clusters put into the passed cluster list
    1123              : // for the given endpoint and client/server polarity
    1124            0 : uint8_t emberAfGetClustersFromEndpoint(EndpointId endpoint, ClusterId * clusterList, uint8_t listLen, bool server)
    1125              : {
    1126            0 :     uint8_t clusterCount = emberAfClusterCount(endpoint, server);
    1127              :     uint8_t i;
    1128              :     const EmberAfCluster * cluster;
    1129            0 :     if (clusterCount > listLen)
    1130              :     {
    1131            0 :         clusterCount = listLen;
    1132              :     }
    1133            0 :     for (i = 0; i < clusterCount; i++)
    1134              :     {
    1135            0 :         cluster        = emberAfGetNthCluster(endpoint, i, server);
    1136            0 :         clusterList[i] = (cluster == nullptr ? kEmberInvalidEndpointIndex : cluster->clusterId);
    1137              :     }
    1138            0 :     return clusterCount;
    1139              : }
    1140              : 
    1141           15 : void emberAfInitializeAttributes(EndpointId endpoint)
    1142              : {
    1143           15 :     emAfLoadAttributeDefaults(endpoint);
    1144           15 : }
    1145              : 
    1146           15 : void emAfLoadAttributeDefaults(EndpointId endpoint, Optional<ClusterId> clusterId)
    1147              : {
    1148              :     uint16_t ep;
    1149              :     uint8_t clusterI;
    1150              :     uint16_t attr;
    1151              :     uint8_t * ptr;
    1152           15 :     uint16_t epCount = emberAfEndpointCount();
    1153              :     uint8_t attrData[ATTRIBUTE_LARGEST];
    1154           15 :     auto * attrStorage = GetAttributePersistenceProvider();
    1155              :     // Don't check whether we actually have an attrStorage here, because it's OK
    1156              :     // to have one if none of our attributes have NVM storage.
    1157              : 
    1158           30 :     for (ep = 0; ep < epCount; ep++)
    1159              :     {
    1160              :         EmberAfDefinedEndpoint * de;
    1161           15 :         if (endpoint != kInvalidEndpointId)
    1162              :         {
    1163            0 :             ep = emberAfIndexFromEndpoint(endpoint);
    1164            0 :             if (ep == kEmberInvalidEndpointIndex)
    1165              :             {
    1166            0 :                 return;
    1167              :             }
    1168              :         }
    1169           15 :         de = &(emAfEndpoints[ep]);
    1170              : 
    1171         1620 :         for (clusterI = 0; clusterI < de->endpointType->clusterCount; clusterI++)
    1172              :         {
    1173         1605 :             const EmberAfCluster * cluster = &(de->endpointType->cluster[clusterI]);
    1174         1605 :             if (clusterId.HasValue())
    1175              :             {
    1176            0 :                 if (clusterId.Value() != cluster->clusterId)
    1177              :                 {
    1178            0 :                     continue;
    1179              :                 }
    1180              :             }
    1181              : 
    1182              :             // when the attributeCount is high, the loop takes too long to run and a
    1183              :             // watchdog kicks in causing a reset. As a workaround, we'll
    1184              :             // conditionally manually reset the watchdog. 300 sounds like a good
    1185              :             // magic number for now.
    1186         1605 :             if (cluster->attributeCount > 300)
    1187              :             {
    1188              :                 // halResetWatchdog();
    1189              :             }
    1190         1605 :             for (attr = 0; attr < cluster->attributeCount; attr++)
    1191              :             {
    1192            0 :                 const EmberAfAttributeMetadata * am = &(cluster->attributes[attr]);
    1193            0 :                 ptr                                 = nullptr; // Will get set to the value to write, as needed.
    1194              : 
    1195              :                 // First check for a persisted value.
    1196            0 :                 if (am->IsAutomaticallyPersisted())
    1197              :                 {
    1198            0 :                     VerifyOrDieWithMsg(attrStorage != nullptr, Zcl, "Attribute persistence needs a persistence provider");
    1199            0 :                     MutableByteSpan bytes(attrData);
    1200              :                     CHIP_ERROR err =
    1201            0 :                         attrStorage->ReadValue(ConcreteAttributePath(de->endpoint, cluster->clusterId, am->attributeId), am, bytes);
    1202            0 :                     if (err == CHIP_NO_ERROR)
    1203              :                     {
    1204            0 :                         ptr = attrData;
    1205              :                     }
    1206              :                     else
    1207              :                     {
    1208            0 :                         ChipLogDetail(
    1209              :                             DataManagement,
    1210              :                             "Failed to read stored attribute (%u, " ChipLogFormatMEI ", " ChipLogFormatMEI ": %" CHIP_ERROR_FORMAT,
    1211              :                             de->endpoint, ChipLogValueMEI(cluster->clusterId), ChipLogValueMEI(am->attributeId), err.Format());
    1212              :                         // Just fall back to default value.
    1213              :                     }
    1214              :                 }
    1215              : 
    1216            0 :                 if (!am->IsExternal())
    1217              :                 {
    1218              :                     EmberAfAttributeSearchRecord record;
    1219            0 :                     record.endpoint    = de->endpoint;
    1220            0 :                     record.clusterId   = cluster->clusterId;
    1221            0 :                     record.attributeId = am->attributeId;
    1222              : 
    1223            0 :                     if (ptr == nullptr)
    1224              :                     {
    1225            0 :                         size_t defaultValueSizeForBigEndianNudger = 0;
    1226              :                         // Bypasses compiler warning about unused variable for little endian platforms.
    1227              :                         (void) defaultValueSizeForBigEndianNudger;
    1228            0 :                         if ((am->mask & ATTRIBUTE_MASK_MIN_MAX) != 0U)
    1229              :                         {
    1230              :                             // This is intentionally 2 and not 4 bytes since defaultValue in min/max
    1231              :                             // attributes is still uint16_t.
    1232            0 :                             if (emberAfAttributeSize(am) <= 2)
    1233              :                             {
    1234              :                                 static_assert(sizeof(am->defaultValue.ptrToMinMaxValue->defaultValue.defaultValue) == 2,
    1235              :                                               "if statement relies on size of max/min defaultValue being 2");
    1236            0 :                                 ptr = (uint8_t *) &(am->defaultValue.ptrToMinMaxValue->defaultValue.defaultValue);
    1237            0 :                                 defaultValueSizeForBigEndianNudger =
    1238              :                                     sizeof(am->defaultValue.ptrToMinMaxValue->defaultValue.defaultValue);
    1239              :                             }
    1240              :                             else
    1241              :                             {
    1242            0 :                                 ptr = (uint8_t *) am->defaultValue.ptrToMinMaxValue->defaultValue.ptrToDefaultValue;
    1243              :                             }
    1244              :                         }
    1245              :                         else
    1246              :                         {
    1247            0 :                             if ((emberAfAttributeSize(am) <= 4) && !emberAfIsStringAttributeType(am->attributeType))
    1248              :                             {
    1249            0 :                                 ptr                                = (uint8_t *) &(am->defaultValue.defaultValue);
    1250            0 :                                 defaultValueSizeForBigEndianNudger = sizeof(am->defaultValue.defaultValue);
    1251              :                             }
    1252              :                             else
    1253              :                             {
    1254            0 :                                 ptr = (uint8_t *) am->defaultValue.ptrToDefaultValue;
    1255              :                             }
    1256              :                         }
    1257              :                         // At this point, ptr either points to a default value, or is NULL, in which case
    1258              :                         // it should be treated as if it is pointing to an array of all zeroes.
    1259              : 
    1260              : #if (CHIP_CONFIG_BIG_ENDIAN_TARGET)
    1261              :                         // The default values for attributes that are less than or equal to
    1262              :                         // defaultValueSizeForBigEndianNudger in bytes are stored in an
    1263              :                         // uint32_t.  On big-endian platforms, a pointer to the default value
    1264              :                         // of size less than defaultValueSizeForBigEndianNudger will point to the wrong
    1265              :                         // byte.  So, for those cases, nudge the pointer forward so it points
    1266              :                         // to the correct byte.
    1267              :                         if (emberAfAttributeSize(am) < defaultValueSizeForBigEndianNudger && ptr != NULL)
    1268              :                         {
    1269              :                             ptr += (defaultValueSizeForBigEndianNudger - emberAfAttributeSize(am));
    1270              :                         }
    1271              : #endif // BIGENDIAN
    1272              :                     }
    1273              : 
    1274            0 :                     emAfReadOrWriteAttribute(&record,
    1275              :                                              nullptr, // metadata - unused
    1276              :                                              ptr,
    1277              :                                              0,     // buffer size - unused
    1278              :                                              true); // write?
    1279              :                 }
    1280              :             }
    1281              :         }
    1282           15 :         if (endpoint != kInvalidEndpointId)
    1283              :         {
    1284            0 :             break;
    1285              :         }
    1286              :     }
    1287              : }
    1288              : 
    1289              : // 'data' argument may be null, since we changed the ptrToDefaultValue
    1290              : // to be null instead of pointing to all zeroes.
    1291              : // This function has to be able to deal with that.
    1292            0 : void emAfSaveAttributeToStorageIfNeeded(uint8_t * data, EndpointId endpoint, ClusterId clusterId,
    1293              :                                         const EmberAfAttributeMetadata * metadata)
    1294              : {
    1295              :     // Get out of here if this attribute isn't marked non-volatile.
    1296            0 :     if (!metadata->IsAutomaticallyPersisted())
    1297              :     {
    1298            0 :         return;
    1299              :     }
    1300              : 
    1301              :     // TODO: Maybe we should have a separate constant for the size of the
    1302              :     // largest non-volatile attribute?
    1303            0 :     uint8_t allZeroData[ATTRIBUTE_LARGEST] = { 0 };
    1304            0 :     if (data == nullptr)
    1305              :     {
    1306            0 :         data = allZeroData;
    1307              :     }
    1308              : 
    1309              :     size_t dataSize;
    1310            0 :     EmberAfAttributeType type = metadata->attributeType;
    1311            0 :     if (emberAfIsStringAttributeType(type))
    1312              :     {
    1313            0 :         dataSize = emberAfStringLength(data) + 1;
    1314              :     }
    1315            0 :     else if (emberAfIsLongStringAttributeType(type))
    1316              :     {
    1317            0 :         dataSize = emberAfLongStringLength(data) + 2;
    1318              :     }
    1319              :     else
    1320              :     {
    1321            0 :         dataSize = metadata->size;
    1322              :     }
    1323              : 
    1324            0 :     auto * attrStorage = GetAttributePersistenceProvider();
    1325            0 :     if (attrStorage)
    1326              :     {
    1327            0 :         attrStorage->WriteValue(ConcreteAttributePath(endpoint, clusterId, metadata->attributeId), ByteSpan(data, dataSize));
    1328              :     }
    1329              :     else
    1330              :     {
    1331            0 :         ChipLogProgress(DataManagement, "Can't store attribute value: no persistence provider");
    1332              :     }
    1333              : }
    1334              : 
    1335              : // This function returns the actual function point from the array,
    1336              : // iterating over the function bits.
    1337         1664 : EmberAfGenericClusterFunction emberAfFindClusterFunction(const EmberAfCluster * cluster, EmberAfClusterMask functionMask)
    1338              : {
    1339         1664 :     EmberAfClusterMask mask = 0x01;
    1340         1664 :     uint8_t functionIndex   = 0;
    1341              : 
    1342         1664 :     if ((cluster->mask & functionMask) == 0)
    1343              :     {
    1344         1664 :         return nullptr;
    1345              :     }
    1346              : 
    1347            0 :     while (mask < functionMask)
    1348              :     {
    1349            0 :         if ((cluster->mask & mask) != 0)
    1350              :         {
    1351            0 :             functionIndex++;
    1352              :         }
    1353            0 :         mask = static_cast<EmberAfClusterMask>(mask << 1);
    1354              :     }
    1355            0 :     return cluster->functions[functionIndex];
    1356              : }
    1357              : 
    1358              : namespace chip {
    1359              : namespace app {
    1360              : 
    1361            0 : CHIP_ERROR SetParentEndpointForEndpoint(EndpointId childEndpoint, EndpointId parentEndpoint)
    1362              : {
    1363            0 :     uint16_t childIndex  = emberAfIndexFromEndpoint(childEndpoint);
    1364            0 :     uint16_t parentIndex = emberAfIndexFromEndpoint(parentEndpoint);
    1365              : 
    1366            0 :     if (childIndex == kEmberInvalidEndpointIndex || parentIndex == kEmberInvalidEndpointIndex)
    1367              :     {
    1368            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
    1369              :     }
    1370            0 :     emAfEndpoints[childIndex].parentEndpointId = parentEndpoint;
    1371            0 :     return CHIP_NO_ERROR;
    1372              : }
    1373              : 
    1374            0 : CHIP_ERROR SetFlatCompositionForEndpoint(EndpointId endpoint)
    1375              : {
    1376            0 :     uint16_t index = emberAfIndexFromEndpoint(endpoint);
    1377            0 :     if (index == kEmberInvalidEndpointIndex)
    1378              :     {
    1379            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
    1380              :     }
    1381            0 :     emAfEndpoints[index].bitmask.Clear(EmberAfEndpointOptions::isTreeComposition);
    1382            0 :     emAfEndpoints[index].bitmask.Set(EmberAfEndpointOptions::isFlatComposition);
    1383            0 :     return CHIP_NO_ERROR;
    1384              : }
    1385              : 
    1386            0 : CHIP_ERROR SetTreeCompositionForEndpoint(EndpointId endpoint)
    1387              : {
    1388            0 :     uint16_t index = emberAfIndexFromEndpoint(endpoint);
    1389            0 :     if (index == kEmberInvalidEndpointIndex)
    1390              :     {
    1391            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
    1392              :     }
    1393            0 :     emAfEndpoints[index].bitmask.Clear(EmberAfEndpointOptions::isFlatComposition);
    1394            0 :     emAfEndpoints[index].bitmask.Set(EmberAfEndpointOptions::isTreeComposition);
    1395            0 :     return CHIP_NO_ERROR;
    1396              : }
    1397              : 
    1398            0 : bool IsFlatCompositionForEndpoint(EndpointId endpoint)
    1399              : {
    1400            0 :     uint16_t index = emberAfIndexFromEndpoint(endpoint);
    1401            0 :     if (index == kEmberInvalidEndpointIndex)
    1402              :     {
    1403            0 :         return false;
    1404              :     }
    1405            0 :     return emAfEndpoints[index].bitmask.Has(EmberAfEndpointOptions::isFlatComposition);
    1406              : }
    1407              : 
    1408            0 : bool IsTreeCompositionForEndpoint(EndpointId endpoint)
    1409              : {
    1410            0 :     uint16_t index = emberAfIndexFromEndpoint(endpoint);
    1411            0 :     if (index == kEmberInvalidEndpointIndex)
    1412              :     {
    1413            0 :         return false;
    1414              :     }
    1415            0 :     return emAfEndpoints[index].bitmask.Has(EmberAfEndpointOptions::isTreeComposition);
    1416              : }
    1417              : 
    1418           47 : EndpointComposition GetCompositionForEndpointIndex(uint16_t endpointIndex)
    1419              : {
    1420           47 :     VerifyOrReturnValue(endpointIndex < ArraySize(emAfEndpoints), EndpointComposition::kInvalid);
    1421           47 :     if (emAfEndpoints[endpointIndex].bitmask.Has(EmberAfEndpointOptions::isFlatComposition))
    1422              :     {
    1423           13 :         return EndpointComposition::kFullFamily;
    1424              :     }
    1425           34 :     if (emAfEndpoints[endpointIndex].bitmask.Has(EmberAfEndpointOptions::isTreeComposition))
    1426              :     {
    1427           34 :         return EndpointComposition::kTree;
    1428              :     }
    1429            0 :     return EndpointComposition::kInvalid;
    1430              : }
    1431              : 
    1432              : } // namespace app
    1433              : } // namespace chip
    1434              : 
    1435            0 : uint16_t emberAfGetServerAttributeCount(EndpointId endpoint, ClusterId cluster)
    1436              : {
    1437            0 :     const EmberAfCluster * clusterObj = emberAfFindServerCluster(endpoint, cluster);
    1438            0 :     VerifyOrReturnError(clusterObj != nullptr, 0);
    1439            0 :     return clusterObj->attributeCount;
    1440              : }
    1441              : 
    1442            0 : uint16_t emberAfGetServerAttributeIndexByAttributeId(EndpointId endpoint, ClusterId cluster, AttributeId attributeId)
    1443              : {
    1444            0 :     const EmberAfCluster * clusterObj = emberAfFindServerCluster(endpoint, cluster);
    1445            0 :     VerifyOrReturnError(clusterObj != nullptr, UINT16_MAX);
    1446              : 
    1447            0 :     for (uint16_t i = 0; i < clusterObj->attributeCount; i++)
    1448              :     {
    1449            0 :         if (clusterObj->attributes[i].attributeId == attributeId)
    1450              :         {
    1451            0 :             return i;
    1452              :         }
    1453              :     }
    1454            0 :     return UINT16_MAX;
    1455              : }
    1456              : 
    1457            0 : Optional<AttributeId> emberAfGetServerAttributeIdByIndex(EndpointId endpoint, ClusterId cluster, uint16_t attributeIndex)
    1458              : {
    1459            0 :     const EmberAfCluster * clusterObj = emberAfFindServerCluster(endpoint, cluster);
    1460            0 :     if (clusterObj == nullptr || clusterObj->attributeCount <= attributeIndex)
    1461              :     {
    1462            0 :         return Optional<AttributeId>::Missing();
    1463              :     }
    1464            0 :     return Optional<AttributeId>(clusterObj->attributes[attributeIndex].attributeId);
    1465              : }
    1466              : 
    1467         5367 : DataVersion * emberAfDataVersionStorage(const ConcreteClusterPath & aConcreteClusterPath)
    1468              : {
    1469         5367 :     uint16_t index = emberAfIndexFromEndpoint(aConcreteClusterPath.mEndpointId);
    1470         5367 :     if (index == kEmberInvalidEndpointIndex)
    1471              :     {
    1472              :         // Unknown endpoint.
    1473           47 :         return nullptr;
    1474              :     }
    1475         5320 :     const EmberAfDefinedEndpoint & ep = emAfEndpoints[index];
    1476         5320 :     if (!ep.dataVersions)
    1477              :     {
    1478              :         // No storage provided.
    1479            0 :         return nullptr;
    1480              :     }
    1481              : 
    1482              :     // This does a second walk over endpoints to find the right one, but
    1483              :     // probably worth it to avoid duplicating code.
    1484         5320 :     auto clusterIndex = emberAfClusterIndex(aConcreteClusterPath.mEndpointId, aConcreteClusterPath.mClusterId, CLUSTER_MASK_SERVER);
    1485         5320 :     if (clusterIndex == 0xFF)
    1486              :     {
    1487              :         // No such cluster on this endpoint.
    1488            0 :         return nullptr;
    1489              :     }
    1490              : 
    1491         5320 :     return ep.dataVersions + clusterIndex;
    1492              : }
    1493              : 
    1494              : namespace {
    1495              : class GlobalInteractionModelEngineChangedpathListener : public AttributesChangedListener
    1496              : {
    1497              : public:
    1498            6 :     ~GlobalInteractionModelEngineChangedpathListener() = default;
    1499              : 
    1500           71 :     void MarkDirty(const AttributePathParams & path) override
    1501              :     {
    1502           71 :         InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(path);
    1503           71 :     }
    1504              : };
    1505              : 
    1506              : } // namespace
    1507              : 
    1508           71 : AttributesChangedListener * emberAfGlobalInteractionModelAttributesChangedListener()
    1509              : {
    1510           71 :     static GlobalInteractionModelEngineChangedpathListener listener;
    1511           71 :     return &listener;
    1512              : }
    1513              : 
    1514         2476 : void emberAfAttributeChanged(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId,
    1515              :                              AttributesChangedListener * listener)
    1516              : {
    1517              :     // Increase cluster data path
    1518         2476 :     DataVersion * version = emberAfDataVersionStorage(ConcreteClusterPath(endpoint, clusterId));
    1519         2476 :     if (version == nullptr)
    1520              :     {
    1521           47 :         ChipLogError(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " not found in IncreaseClusterDataVersion!", endpoint,
    1522              :                      ChipLogValueMEI(clusterId));
    1523              :     }
    1524              :     else
    1525              :     {
    1526         2429 :         (*(version))++;
    1527         2429 :         ChipLogDetail(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " update version to %" PRIx32, endpoint,
    1528              :                       ChipLogValueMEI(clusterId), *(version));
    1529              :     }
    1530              : 
    1531         2476 :     listener->MarkDirty(AttributePathParams(endpoint, clusterId, attributeId));
    1532         2476 : }
    1533              : 
    1534           24 : void emberAfEndpointChanged(EndpointId endpoint, AttributesChangedListener * listener)
    1535              : {
    1536           24 :     listener->MarkDirty(AttributePathParams(endpoint));
    1537           24 : }
        

Generated by: LCOV version 2.0-1