LCOV - code coverage report
Current view: top level - app/util - ember-compatibility-functions.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 161 464 34.7 %
Date: 2024-02-15 08:20:41 Functions: 20 68 29.4 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2021-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             : /**
      19             :  *    @file
      20             :  *          Contains the functions for compatibility with ember ZCL inner state
      21             :  *          when calling ember callbacks.
      22             :  */
      23             : 
      24             : #include <access/AccessControl.h>
      25             : #include <app/CommandHandlerInterface.h>
      26             : #include <app/ConcreteAttributePath.h>
      27             : #include <app/ConcreteEventPath.h>
      28             : #include <app/GlobalAttributes.h>
      29             : #include <app/InteractionModelEngine.h>
      30             : #include <app/RequiredPrivilege.h>
      31             : #include <app/att-storage.h>
      32             : #include <app/reporting/Engine.h>
      33             : #include <app/reporting/reporting.h>
      34             : #include <app/util/af.h>
      35             : #include <app/util/attribute-storage-null-handling.h>
      36             : #include <app/util/attribute-storage.h>
      37             : #include <app/util/attribute-table.h>
      38             : #include <app/util/config.h>
      39             : #include <app/util/odd-sized-integers.h>
      40             : #include <app/util/util.h>
      41             : #include <lib/core/CHIPCore.h>
      42             : #include <lib/core/TLV.h>
      43             : #include <lib/support/CodeUtils.h>
      44             : #include <lib/support/SafeInt.h>
      45             : #include <lib/support/TypeTraits.h>
      46             : #include <platform/LockTracker.h>
      47             : #include <protocols/interaction_model/Constants.h>
      48             : 
      49             : #include <app-common/zap-generated/attribute-type.h>
      50             : 
      51             : #include <zap-generated/endpoint_config.h>
      52             : 
      53             : #include <limits>
      54             : 
      55             : using chip::Protocols::InteractionModel::Status;
      56             : 
      57             : using namespace chip;
      58             : using namespace chip::app;
      59             : using namespace chip::Access;
      60             : 
      61             : namespace chip {
      62             : namespace app {
      63             : namespace Compatibility {
      64             : namespace {
      65             : // On some apps, ATTRIBUTE_LARGEST can as small as 3, making compiler unhappy since data[kAttributeReadBufferSize] cannot hold
      66             : // uint64_t. Make kAttributeReadBufferSize at least 8 so it can fit all basic types.
      67             : constexpr size_t kAttributeReadBufferSize = (ATTRIBUTE_LARGEST >= 8 ? ATTRIBUTE_LARGEST : 8);
      68             : 
      69             : // BasicType maps the type to basic int(8|16|32|64)(s|u) types.
      70           0 : EmberAfAttributeType BaseType(EmberAfAttributeType type)
      71             : {
      72           0 :     switch (type)
      73             :     {
      74           0 :     case ZCL_ACTION_ID_ATTRIBUTE_TYPE:  // Action Id
      75             :     case ZCL_FABRIC_IDX_ATTRIBUTE_TYPE: // Fabric Index
      76             :     case ZCL_BITMAP8_ATTRIBUTE_TYPE:    // 8-bit bitmap
      77             :     case ZCL_ENUM8_ATTRIBUTE_TYPE:      // 8-bit enumeration
      78             :     case ZCL_STATUS_ATTRIBUTE_TYPE:     // Status Code
      79             :     case ZCL_PERCENT_ATTRIBUTE_TYPE:    // Percentage
      80             :         static_assert(std::is_same<chip::Percent, uint8_t>::value,
      81             :                       "chip::Percent is expected to be uint8_t, change this when necessary");
      82           0 :         return ZCL_INT8U_ATTRIBUTE_TYPE;
      83             : 
      84           0 :     case ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE:   // Endpoint Number
      85             :     case ZCL_GROUP_ID_ATTRIBUTE_TYPE:      // Group Id
      86             :     case ZCL_VENDOR_ID_ATTRIBUTE_TYPE:     // Vendor Id
      87             :     case ZCL_ENUM16_ATTRIBUTE_TYPE:        // 16-bit enumeration
      88             :     case ZCL_BITMAP16_ATTRIBUTE_TYPE:      // 16-bit bitmap
      89             :     case ZCL_PERCENT100THS_ATTRIBUTE_TYPE: // 100ths of a percent
      90             :         static_assert(std::is_same<chip::EndpointId, uint16_t>::value,
      91             :                       "chip::EndpointId is expected to be uint16_t, change this when necessary");
      92             :         static_assert(std::is_same<chip::GroupId, uint16_t>::value,
      93             :                       "chip::GroupId is expected to be uint16_t, change this when necessary");
      94             :         static_assert(std::is_same<chip::Percent100ths, uint16_t>::value,
      95             :                       "chip::Percent100ths is expected to be uint16_t, change this when necessary");
      96           0 :         return ZCL_INT16U_ATTRIBUTE_TYPE;
      97             : 
      98           0 :     case ZCL_CLUSTER_ID_ATTRIBUTE_TYPE: // Cluster Id
      99             :     case ZCL_ATTRIB_ID_ATTRIBUTE_TYPE:  // Attribute Id
     100             :     case ZCL_FIELD_ID_ATTRIBUTE_TYPE:   // Field Id
     101             :     case ZCL_EVENT_ID_ATTRIBUTE_TYPE:   // Event Id
     102             :     case ZCL_COMMAND_ID_ATTRIBUTE_TYPE: // Command Id
     103             :     case ZCL_TRANS_ID_ATTRIBUTE_TYPE:   // Transaction Id
     104             :     case ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE: // Device Type Id
     105             :     case ZCL_DATA_VER_ATTRIBUTE_TYPE:   // Data Version
     106             :     case ZCL_BITMAP32_ATTRIBUTE_TYPE:   // 32-bit bitmap
     107             :     case ZCL_EPOCH_S_ATTRIBUTE_TYPE:    // Epoch Seconds
     108             :     case ZCL_ELAPSED_S_ATTRIBUTE_TYPE:  // Elapsed Seconds
     109             :         static_assert(std::is_same<chip::ClusterId, uint32_t>::value,
     110             :                       "chip::Cluster is expected to be uint32_t, change this when necessary");
     111             :         static_assert(std::is_same<chip::AttributeId, uint32_t>::value,
     112             :                       "chip::AttributeId is expected to be uint32_t, change this when necessary");
     113             :         static_assert(std::is_same<chip::AttributeId, uint32_t>::value,
     114             :                       "chip::AttributeId is expected to be uint32_t, change this when necessary");
     115             :         static_assert(std::is_same<chip::EventId, uint32_t>::value,
     116             :                       "chip::EventId is expected to be uint32_t, change this when necessary");
     117             :         static_assert(std::is_same<chip::CommandId, uint32_t>::value,
     118             :                       "chip::CommandId is expected to be uint32_t, change this when necessary");
     119             :         static_assert(std::is_same<chip::TransactionId, uint32_t>::value,
     120             :                       "chip::TransactionId is expected to be uint32_t, change this when necessary");
     121             :         static_assert(std::is_same<chip::DeviceTypeId, uint32_t>::value,
     122             :                       "chip::DeviceTypeId is expected to be uint32_t, change this when necessary");
     123             :         static_assert(std::is_same<chip::DataVersion, uint32_t>::value,
     124             :                       "chip::DataVersion is expected to be uint32_t, change this when necessary");
     125           0 :         return ZCL_INT32U_ATTRIBUTE_TYPE;
     126             : 
     127           0 :     case ZCL_AMPERAGE_MA_ATTRIBUTE_TYPE: // Amperage milliamps
     128             :     case ZCL_ENERGY_MWH_ATTRIBUTE_TYPE:  // Energy milliwatt-hours
     129             :     case ZCL_POWER_MW_ATTRIBUTE_TYPE:    // Power milliwatts
     130             :     case ZCL_VOLTAGE_MV_ATTRIBUTE_TYPE:  // Voltage millivolts
     131           0 :         return ZCL_INT64S_ATTRIBUTE_TYPE;
     132             : 
     133           0 :     case ZCL_EVENT_NO_ATTRIBUTE_TYPE:   // Event Number
     134             :     case ZCL_FABRIC_ID_ATTRIBUTE_TYPE:  // Fabric Id
     135             :     case ZCL_NODE_ID_ATTRIBUTE_TYPE:    // Node Id
     136             :     case ZCL_BITMAP64_ATTRIBUTE_TYPE:   // 64-bit bitmap
     137             :     case ZCL_EPOCH_US_ATTRIBUTE_TYPE:   // Epoch Microseconds
     138             :     case ZCL_POSIX_MS_ATTRIBUTE_TYPE:   // POSIX Milliseconds
     139             :     case ZCL_SYSTIME_MS_ATTRIBUTE_TYPE: // System time Milliseconds
     140             :     case ZCL_SYSTIME_US_ATTRIBUTE_TYPE: // System time Microseconds
     141             :         static_assert(std::is_same<chip::EventNumber, uint64_t>::value,
     142             :                       "chip::EventNumber is expected to be uint64_t, change this when necessary");
     143             :         static_assert(std::is_same<chip::FabricId, uint64_t>::value,
     144             :                       "chip::FabricId is expected to be uint64_t, change this when necessary");
     145             :         static_assert(std::is_same<chip::NodeId, uint64_t>::value,
     146             :                       "chip::NodeId is expected to be uint64_t, change this when necessary");
     147           0 :         return ZCL_INT64U_ATTRIBUTE_TYPE;
     148             : 
     149           0 :     case ZCL_TEMPERATURE_ATTRIBUTE_TYPE: // Temperature
     150           0 :         return ZCL_INT16S_ATTRIBUTE_TYPE;
     151             : 
     152           0 :     default:
     153           0 :         return type;
     154             :     }
     155             : }
     156             : 
     157             : } // namespace
     158             : 
     159             : } // namespace Compatibility
     160             : 
     161             : using namespace chip::app::Compatibility;
     162             : 
     163             : namespace {
     164             : // Common buffer for ReadSingleClusterData & WriteSingleClusterData
     165             : uint8_t attributeData[kAttributeReadBufferSize];
     166             : 
     167             : template <typename T>
     168           0 : CHIP_ERROR attributeBufferToNumericTlvData(TLV::TLVWriter & writer, bool isNullable)
     169             : {
     170             :     typename NumericAttributeTraits<T>::StorageType value;
     171           0 :     memcpy(&value, attributeData, sizeof(value));
     172           0 :     TLV::Tag tag = TLV::ContextTag(AttributeDataIB::Tag::kData);
     173           0 :     if (isNullable && NumericAttributeTraits<T>::IsNullValue(value))
     174             :     {
     175           0 :         return writer.PutNull(tag);
     176             :     }
     177             : 
     178           0 :     if (!NumericAttributeTraits<T>::CanRepresentValue(isNullable, value))
     179             :     {
     180           0 :         return CHIP_ERROR_INCORRECT_STATE;
     181             :     }
     182             : 
     183           0 :     return NumericAttributeTraits<T>::Encode(writer, tag, value);
     184             : }
     185             : 
     186             : } // anonymous namespace
     187             : 
     188           7 : Protocols::InteractionModel::Status ServerClusterCommandExists(const ConcreteCommandPath & aCommandPath)
     189             : {
     190             :     using Protocols::InteractionModel::Status;
     191             : 
     192           7 :     const EmberAfEndpointType * type = emberAfFindEndpointType(aCommandPath.mEndpointId);
     193           7 :     if (type == nullptr)
     194             :     {
     195           1 :         return Status::UnsupportedEndpoint;
     196             :     }
     197             : 
     198           6 :     const EmberAfCluster * cluster = emberAfFindClusterInType(type, aCommandPath.mClusterId, CLUSTER_MASK_SERVER);
     199           6 :     if (cluster == nullptr)
     200             :     {
     201           0 :         return Status::UnsupportedCluster;
     202             :     }
     203             : 
     204             :     auto * commandHandler =
     205           6 :         InteractionModelEngine::GetInstance()->FindCommandHandler(aCommandPath.mEndpointId, aCommandPath.mClusterId);
     206           6 :     if (commandHandler)
     207             :     {
     208             :         struct Context
     209             :         {
     210             :             bool commandExists;
     211             :             CommandId targetCommand;
     212           6 :         } context{ false, aCommandPath.mCommandId };
     213             : 
     214           6 :         CHIP_ERROR err = commandHandler->EnumerateAcceptedCommands(
     215             :             aCommandPath,
     216           2 :             [](CommandId command, void * closure) -> Loop {
     217           2 :                 auto * ctx = static_cast<Context *>(closure);
     218           2 :                 if (ctx->targetCommand == command)
     219             :                 {
     220           2 :                     ctx->commandExists = true;
     221           2 :                     return Loop::Break;
     222             :                 }
     223           0 :                 return Loop::Continue;
     224             :             },
     225             :             &context);
     226             : 
     227             :         // We now have three cases:
     228             :         // 1) handler returned CHIP_ERROR_NOT_IMPLEMENTED.  In that case we
     229             :         //    should fall back to looking at cluster->acceptedCommandList
     230             :         // 2) handler returned success.  In that case, the handler is the source
     231             :         //    of truth about the set of accepted commands, and
     232             :         //    context.commandExists indicates whether a aCommandPath.mCommandId
     233             :         //    was in the set, and we should return either Success or
     234             :         //    UnsupportedCommand accordingly.
     235             :         // 3) Some other status was returned.  In this case we should probably
     236             :         //    err on the side of not allowing the command, since we have no idea
     237             :         //    whether to allow it or not.
     238           6 :         if (err != CHIP_ERROR_NOT_IMPLEMENTED)
     239             :         {
     240           3 :             if (err == CHIP_NO_ERROR)
     241             :             {
     242           3 :                 return context.commandExists ? Status::Success : Status::UnsupportedCommand;
     243             :             }
     244             : 
     245           0 :             return Status::Failure;
     246             :         }
     247             :     }
     248             : 
     249           3 :     for (const CommandId * cmd = cluster->acceptedCommandList; cmd != nullptr && *cmd != kInvalidCommandId; cmd++)
     250             :     {
     251           1 :         if (*cmd == aCommandPath.mCommandId)
     252             :         {
     253           1 :             return Status::Success;
     254             :         }
     255             :     }
     256             : 
     257           2 :     return Status::UnsupportedCommand;
     258             : }
     259             : 
     260             : namespace {
     261             : 
     262        2471 : CHIP_ERROR ReadClusterDataVersion(const ConcreteClusterPath & aConcreteClusterPath, DataVersion & aDataVersion)
     263             : {
     264        2471 :     DataVersion * version = emberAfDataVersionStorage(aConcreteClusterPath);
     265        2471 :     if (version == nullptr)
     266             :     {
     267           0 :         ChipLogError(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " not found in ReadClusterDataVersion!",
     268             :                      aConcreteClusterPath.mEndpointId, ChipLogValueMEI(aConcreteClusterPath.mClusterId));
     269           0 :         return CHIP_ERROR_NOT_FOUND;
     270             :     }
     271        2471 :     aDataVersion = *version;
     272        2471 :     return CHIP_NO_ERROR;
     273             : }
     274             : 
     275        2476 : void IncreaseClusterDataVersion(const ConcreteClusterPath & aConcreteClusterPath)
     276             : {
     277        2476 :     DataVersion * version = emberAfDataVersionStorage(aConcreteClusterPath);
     278        2476 :     if (version == nullptr)
     279             :     {
     280          47 :         ChipLogError(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " not found in IncreaseClusterDataVersion!",
     281             :                      aConcreteClusterPath.mEndpointId, ChipLogValueMEI(aConcreteClusterPath.mClusterId));
     282             :     }
     283             :     else
     284             :     {
     285        2429 :         (*(version))++;
     286        2429 :         ChipLogDetail(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " update version to %" PRIx32,
     287             :                       aConcreteClusterPath.mEndpointId, ChipLogValueMEI(aConcreteClusterPath.mClusterId), *(version));
     288             :     }
     289        2476 : }
     290             : 
     291           0 : CHIP_ERROR SendSuccessStatus(AttributeReportIB::Builder & aAttributeReport, AttributeDataIB::Builder & aAttributeDataIBBuilder)
     292             : {
     293           0 :     ReturnErrorOnFailure(aAttributeDataIBBuilder.EndOfAttributeDataIB());
     294           0 :     return aAttributeReport.EndOfAttributeReportIB();
     295             : }
     296             : 
     297           0 : CHIP_ERROR SendFailureStatus(const ConcreteAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports,
     298             :                              Protocols::InteractionModel::Status aStatus, TLV::TLVWriter * aReportCheckpoint)
     299             : {
     300           0 :     if (aReportCheckpoint != nullptr)
     301             :     {
     302           0 :         aAttributeReports.Rollback(*aReportCheckpoint);
     303             :     }
     304           0 :     return aAttributeReports.EncodeAttributeStatus(aPath, StatusIB(aStatus));
     305             : }
     306             : 
     307             : // This reader should never actually be registered; we do manual dispatch to it
     308             : // for the one attribute it handles.
     309             : class MandatoryGlobalAttributeReader : public AttributeAccessInterface
     310             : {
     311             : public:
     312        2471 :     MandatoryGlobalAttributeReader(const EmberAfCluster * aCluster) :
     313        2471 :         AttributeAccessInterface(MakeOptional(kInvalidEndpointId), kInvalidClusterId), mCluster(aCluster)
     314        2471 :     {}
     315             : 
     316             : protected:
     317             :     const EmberAfCluster * mCluster;
     318             : };
     319             : 
     320             : class GlobalAttributeReader : public MandatoryGlobalAttributeReader
     321             : {
     322             : public:
     323        2471 :     GlobalAttributeReader(const EmberAfCluster * aCluster) : MandatoryGlobalAttributeReader(aCluster) {}
     324             : 
     325             :     CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
     326             : 
     327             : private:
     328             :     typedef CHIP_ERROR (CommandHandlerInterface::*CommandListEnumerator)(const ConcreteClusterPath & cluster,
     329             :                                                                          CommandHandlerInterface::CommandIdCallback callback,
     330             :                                                                          void * context);
     331             :     static CHIP_ERROR EncodeCommandList(const ConcreteClusterPath & aClusterPath, AttributeValueEncoder & aEncoder,
     332             :                                         CommandListEnumerator aEnumerator, const CommandId * aClusterCommandList);
     333             : };
     334             : 
     335         715 : CHIP_ERROR GlobalAttributeReader::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
     336             : {
     337             :     using namespace Clusters::Globals::Attributes;
     338         715 :     switch (aPath.mAttributeId)
     339             :     {
     340         279 :     case AttributeList::Id:
     341         279 :         return aEncoder.EncodeList([this](const auto & encoder) {
     342         249 :             const size_t count     = mCluster->attributeCount;
     343         249 :             bool addedExtraGlobals = false;
     344        1629 :             for (size_t i = 0; i < count; ++i)
     345             :             {
     346        1424 :                 AttributeId id              = mCluster->attributes[i].attributeId;
     347        1424 :                 constexpr auto lastGlobalId = GlobalAttributesNotInMetadata[ArraySize(GlobalAttributesNotInMetadata) - 1];
     348             : #if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
     349             :                 // The GlobalAttributesNotInMetadata shouldn't have any gaps in their ids here.
     350             :                 static_assert(lastGlobalId - GlobalAttributesNotInMetadata[0] == ArraySize(GlobalAttributesNotInMetadata) - 1,
     351             :                               "Ids in GlobalAttributesNotInMetadata not consecutive");
     352             : #else
     353             :                 // If EventList is not supported. The GlobalAttributesNotInMetadata is missing one id here.
     354             :                 static_assert(lastGlobalId - GlobalAttributesNotInMetadata[0] == ArraySize(GlobalAttributesNotInMetadata),
     355             :                               "Ids in GlobalAttributesNotInMetadata not consecutive (except EventList)");
     356             : #endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
     357        1424 :                 if (!addedExtraGlobals && id > lastGlobalId)
     358             :                 {
     359         880 :                     for (const auto & globalId : GlobalAttributesNotInMetadata)
     360             :                     {
     361         669 :                         ReturnErrorOnFailure(encoder.Encode(globalId));
     362             :                     }
     363         211 :                     addedExtraGlobals = true;
     364             :                 }
     365        1406 :                 ReturnErrorOnFailure(encoder.Encode(id));
     366             :             }
     367         205 :             if (!addedExtraGlobals)
     368             :             {
     369           0 :                 for (const auto & globalId : GlobalAttributesNotInMetadata)
     370             :                 {
     371           0 :                     ReturnErrorOnFailure(encoder.Encode(globalId));
     372             :                 }
     373             :             }
     374         205 :             return CHIP_NO_ERROR;
     375         279 :         });
     376             : #if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
     377             :     case EventList::Id:
     378             :         return aEncoder.EncodeList([this](const auto & encoder) {
     379             :             for (size_t i = 0; i < mCluster->eventCount; ++i)
     380             :             {
     381             :                 ReturnErrorOnFailure(encoder.Encode(mCluster->eventList[i]));
     382             :             }
     383             :             return CHIP_NO_ERROR;
     384             :         });
     385             : #endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
     386         231 :     case AcceptedCommandList::Id:
     387         231 :         return EncodeCommandList(aPath, aEncoder, &CommandHandlerInterface::EnumerateAcceptedCommands,
     388         231 :                                  mCluster->acceptedCommandList);
     389         205 :     case GeneratedCommandList::Id:
     390         205 :         return EncodeCommandList(aPath, aEncoder, &CommandHandlerInterface::EnumerateGeneratedCommands,
     391         205 :                                  mCluster->generatedCommandList);
     392           0 :     default:
     393             :         // This function is only called if attributeCluster is non-null in
     394             :         // ReadSingleClusterData, which only happens for attributes listed in
     395             :         // GlobalAttributesNotInMetadata.  If we reach this code, someone added
     396             :         // a global attribute to that list but not the above switch.
     397           0 :         VerifyOrDieWithMsg(false, DataManagement, "Unexpected global attribute: " ChipLogFormatMEI,
     398             :                            ChipLogValueMEI(aPath.mAttributeId));
     399             :         return CHIP_NO_ERROR;
     400             :     }
     401             : }
     402             : 
     403         436 : CHIP_ERROR GlobalAttributeReader::EncodeCommandList(const ConcreteClusterPath & aClusterPath, AttributeValueEncoder & aEncoder,
     404             :                                                     GlobalAttributeReader::CommandListEnumerator aEnumerator,
     405             :                                                     const CommandId * aClusterCommandList)
     406             : {
     407         436 :     return aEncoder.EncodeList([&](const auto & encoder) {
     408             :         auto * commandHandler =
     409         416 :             InteractionModelEngine::GetInstance()->FindCommandHandler(aClusterPath.mEndpointId, aClusterPath.mClusterId);
     410         416 :         if (commandHandler)
     411             :         {
     412             :             struct Context
     413             :             {
     414             :                 decltype(encoder) & commandIdEncoder;
     415             :                 CHIP_ERROR err;
     416           6 :             } context{ encoder, CHIP_NO_ERROR };
     417           6 :             CHIP_ERROR err = (commandHandler->*aEnumerator)(
     418             :                 aClusterPath,
     419           2 :                 [](CommandId command, void * closure) -> Loop {
     420           2 :                     auto * ctx = static_cast<Context *>(closure);
     421           2 :                     ctx->err   = ctx->commandIdEncoder.Encode(command);
     422           2 :                     if (ctx->err != CHIP_NO_ERROR)
     423             :                     {
     424           0 :                         return Loop::Break;
     425             :                     }
     426           2 :                     return Loop::Continue;
     427             :                 },
     428             :                 &context);
     429           6 :             if (err != CHIP_ERROR_NOT_IMPLEMENTED)
     430             :             {
     431           3 :                 return context.err;
     432             :             }
     433             :             // Else fall through to the list in aClusterCommandList.
     434             :         }
     435             : 
     436         414 :         for (const CommandId * cmd = aClusterCommandList; cmd != nullptr && *cmd != kInvalidCommandId; cmd++)
     437             :         {
     438           1 :             ReturnErrorOnFailure(encoder.Encode(*cmd));
     439             :         }
     440         413 :         return CHIP_NO_ERROR;
     441         436 :     });
     442             : }
     443             : 
     444             : // Helper function for trying to read an attribute value via an
     445             : // AttributeAccessInterface.  On failure, the read has failed.  On success, the
     446             : // aTriedEncode outparam is set to whether the AttributeAccessInterface tried to encode a value.
     447        2471 : CHIP_ERROR ReadViaAccessInterface(FabricIndex aAccessingFabricIndex, bool aIsFabricFiltered,
     448             :                                   const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports,
     449             :                                   AttributeValueEncoder::AttributeEncodeState * aEncoderState,
     450             :                                   AttributeAccessInterface * aAccessInterface, bool * aTriedEncode)
     451             : {
     452             :     AttributeValueEncoder::AttributeEncodeState state =
     453        2471 :         (aEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *aEncoderState);
     454        2471 :     DataVersion version = 0;
     455        2471 :     ReturnErrorOnFailure(ReadClusterDataVersion(aPath, version));
     456        2471 :     AttributeValueEncoder valueEncoder(aAttributeReports, aAccessingFabricIndex, aPath, version, aIsFabricFiltered, state);
     457        2471 :     CHIP_ERROR err = aAccessInterface->Read(aPath, valueEncoder);
     458             : 
     459        2471 :     if (err == CHIP_IM_GLOBAL_STATUS(UnsupportedRead) && aPath.mExpanded)
     460             :     {
     461             :         //
     462             :         // Set this to true to ensure our caller will return immediately without proceeding further.
     463             :         //
     464           0 :         *aTriedEncode = true;
     465           0 :         return CHIP_NO_ERROR;
     466             :     }
     467             : 
     468        2471 :     if (err != CHIP_NO_ERROR)
     469             :     {
     470             :         // If the err is not CHIP_NO_ERROR, means the encoding was aborted, then the valueEncoder may save its state.
     471             :         // The state is used by list chunking feature for now.
     472         228 :         if (aEncoderState != nullptr)
     473             :         {
     474         228 :             *aEncoderState = valueEncoder.GetState();
     475             :         }
     476         228 :         return err;
     477             :     }
     478             : 
     479        2243 :     *aTriedEncode = valueEncoder.TriedEncode();
     480        2243 :     return CHIP_NO_ERROR;
     481        2471 : }
     482             : 
     483             : // Determine the appropriate status response for an unsupported attribute for
     484             : // the given path.  Must be called when the attribute is known to be unsupported
     485             : // (i.e. we found no attribute metadata for it).
     486           0 : Protocols::InteractionModel::Status UnsupportedAttributeStatus(const ConcreteAttributePath & aPath)
     487             : {
     488             :     using Protocols::InteractionModel::Status;
     489             : 
     490           0 :     const EmberAfEndpointType * type = emberAfFindEndpointType(aPath.mEndpointId);
     491           0 :     if (type == nullptr)
     492             :     {
     493           0 :         return Status::UnsupportedEndpoint;
     494             :     }
     495             : 
     496           0 :     const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, CLUSTER_MASK_SERVER);
     497           0 :     if (cluster == nullptr)
     498             :     {
     499           0 :         return Status::UnsupportedCluster;
     500             :     }
     501             : 
     502             :     // Since we know the attribute is unsupported and the endpoint/cluster are
     503             :     // OK, this is the only option left.
     504           0 :     return Status::UnsupportedAttribute;
     505             : }
     506             : 
     507             : // Will set at most one of the out-params (aAttributeCluster or
     508             : // aAttributeMetadata) to non-null.  Both null means attribute not supported,
     509             : // aAttributeCluster non-null means this is a supported global attribute that
     510             : // does not have metadata.
     511        4905 : void FindAttributeMetadata(const ConcreteAttributePath & aPath, const EmberAfCluster ** aAttributeCluster,
     512             :                            const EmberAfAttributeMetadata ** aAttributeMetadata)
     513             : {
     514        4905 :     *aAttributeCluster  = nullptr;
     515        4905 :     *aAttributeMetadata = nullptr;
     516             : 
     517       18264 :     for (auto & attr : GlobalAttributesNotInMetadata)
     518             :     {
     519       14074 :         if (attr == aPath.mAttributeId)
     520             :         {
     521         715 :             *aAttributeCluster = emberAfFindServerCluster(aPath.mEndpointId, aPath.mClusterId);
     522         715 :             return;
     523             :         }
     524             :     }
     525             : 
     526        4190 :     *aAttributeMetadata = emberAfLocateAttributeMetadata(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId);
     527             : }
     528             : 
     529             : } // anonymous namespace
     530             : 
     531           3 : bool ConcreteAttributePathExists(const ConcreteAttributePath & aPath)
     532             : {
     533          12 :     for (auto & attr : GlobalAttributesNotInMetadata)
     534             :     {
     535           9 :         if (attr == aPath.mAttributeId)
     536             :         {
     537           0 :             return (emberAfFindServerCluster(aPath.mEndpointId, aPath.mClusterId) != nullptr);
     538             :         }
     539             :     }
     540           3 :     return (emberAfLocateAttributeMetadata(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId) != nullptr);
     541             : }
     542             : 
     543        2471 : CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered,
     544             :                                  const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports,
     545             :                                  AttributeValueEncoder::AttributeEncodeState * apEncoderState)
     546             : {
     547        2471 :     ChipLogDetail(DataManagement,
     548             :                   "Reading attribute: Cluster=" ChipLogFormatMEI " Endpoint=%x AttributeId=" ChipLogFormatMEI " (expanded=%d)",
     549             :                   ChipLogValueMEI(aPath.mClusterId), aPath.mEndpointId, ChipLogValueMEI(aPath.mAttributeId), aPath.mExpanded);
     550             : 
     551             :     // Check attribute existence. This includes attributes with registered metadata, but also specially handled
     552             :     // mandatory global attributes (which just check for cluster on endpoint).
     553             : 
     554        2471 :     const EmberAfCluster * attributeCluster            = nullptr;
     555        2471 :     const EmberAfAttributeMetadata * attributeMetadata = nullptr;
     556        2471 :     FindAttributeMetadata(aPath, &attributeCluster, &attributeMetadata);
     557             : 
     558        2471 :     if (attributeCluster == nullptr && attributeMetadata == nullptr)
     559             :     {
     560           0 :         return SendFailureStatus(aPath, aAttributeReports, UnsupportedAttributeStatus(aPath), nullptr);
     561             :     }
     562             : 
     563             :     // Check access control. A failed check will disallow the operation, and may or may not generate an attribute report
     564             :     // depending on whether the path was expanded.
     565             : 
     566             :     {
     567        2471 :         Access::RequestPath requestPath{ .cluster = aPath.mClusterId, .endpoint = aPath.mEndpointId };
     568        2471 :         Access::Privilege requestPrivilege = RequiredPrivilege::ForReadAttribute(aPath);
     569        2471 :         CHIP_ERROR err                     = Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, requestPrivilege);
     570        2471 :         if (err != CHIP_NO_ERROR)
     571             :         {
     572           0 :             ReturnErrorCodeIf(err != CHIP_ERROR_ACCESS_DENIED, err);
     573           0 :             if (aPath.mExpanded)
     574             :             {
     575           0 :                 return CHIP_NO_ERROR;
     576             :             }
     577             : 
     578           0 :             return SendFailureStatus(aPath, aAttributeReports, Protocols::InteractionModel::Status::UnsupportedAccess, nullptr);
     579             :         }
     580             :     }
     581             : 
     582             :     {
     583             :         // Special handling for mandatory global attributes: these are always for attribute list, using a special
     584             :         // reader (which can be lightweight constructed even from nullptr).
     585        2471 :         GlobalAttributeReader reader(attributeCluster);
     586             :         AttributeAccessInterface * attributeOverride =
     587        2471 :             (attributeCluster != nullptr) ? &reader : GetAttributeAccessOverride(aPath.mEndpointId, aPath.mClusterId);
     588        2471 :         if (attributeOverride)
     589             :         {
     590        2471 :             bool triedEncode = false;
     591        2471 :             ReturnErrorOnFailure(ReadViaAccessInterface(aSubjectDescriptor.fabricIndex, aIsFabricFiltered, aPath, aAttributeReports,
     592             :                                                         apEncoderState, attributeOverride, &triedEncode));
     593        2243 :             ReturnErrorCodeIf(triedEncode, CHIP_NO_ERROR);
     594             :         }
     595        2471 :     }
     596             : 
     597             :     // Read attribute using Ember, if it doesn't have an override.
     598             : 
     599           0 :     TLV::TLVWriter backup;
     600           0 :     aAttributeReports.Checkpoint(backup);
     601             : 
     602           0 :     AttributeReportIB::Builder & attributeReport = aAttributeReports.CreateAttributeReport();
     603           0 :     ReturnErrorOnFailure(aAttributeReports.GetError());
     604             : 
     605           0 :     AttributeDataIB::Builder & attributeDataIBBuilder = attributeReport.CreateAttributeData();
     606           0 :     ReturnErrorOnFailure(attributeDataIBBuilder.GetError());
     607             : 
     608           0 :     DataVersion version = 0;
     609           0 :     ReturnErrorOnFailure(ReadClusterDataVersion(aPath, version));
     610           0 :     attributeDataIBBuilder.DataVersion(version);
     611           0 :     ReturnErrorOnFailure(attributeDataIBBuilder.GetError());
     612             : 
     613           0 :     AttributePathIB::Builder & attributePathIBBuilder = attributeDataIBBuilder.CreatePath();
     614           0 :     ReturnErrorOnFailure(attributeDataIBBuilder.GetError());
     615             : 
     616           0 :     CHIP_ERROR err = attributePathIBBuilder.Endpoint(aPath.mEndpointId)
     617           0 :                          .Cluster(aPath.mClusterId)
     618           0 :                          .Attribute(aPath.mAttributeId)
     619           0 :                          .EndOfAttributePathIB();
     620           0 :     ReturnErrorOnFailure(err);
     621             : 
     622             :     EmberAfAttributeSearchRecord record;
     623           0 :     record.endpoint    = aPath.mEndpointId;
     624           0 :     record.clusterId   = aPath.mClusterId;
     625           0 :     record.attributeId = aPath.mAttributeId;
     626           0 :     Status status      = emAfReadOrWriteAttribute(&record, &attributeMetadata, attributeData, sizeof(attributeData),
     627             :                                                   /* write = */ false);
     628             : 
     629           0 :     if (status == Status::Success)
     630             :     {
     631           0 :         EmberAfAttributeType attributeType = attributeMetadata->attributeType;
     632           0 :         bool isNullable                    = attributeMetadata->IsNullable();
     633           0 :         TLV::TLVWriter * writer            = attributeDataIBBuilder.GetWriter();
     634           0 :         VerifyOrReturnError(writer != nullptr, CHIP_NO_ERROR);
     635           0 :         TLV::Tag tag = TLV::ContextTag(AttributeDataIB::Tag::kData);
     636           0 :         switch (BaseType(attributeType))
     637             :         {
     638           0 :         case ZCL_NO_DATA_ATTRIBUTE_TYPE: // No data
     639           0 :             ReturnErrorOnFailure(writer->PutNull(tag));
     640           0 :             break;
     641           0 :         case ZCL_BOOLEAN_ATTRIBUTE_TYPE: // Boolean
     642           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<bool>(*writer, isNullable));
     643           0 :             break;
     644           0 :         case ZCL_INT8U_ATTRIBUTE_TYPE: // Unsigned 8-bit integer
     645           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<uint8_t>(*writer, isNullable));
     646           0 :             break;
     647           0 :         case ZCL_INT16U_ATTRIBUTE_TYPE: // Unsigned 16-bit integer
     648             :         {
     649           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<uint16_t>(*writer, isNullable));
     650           0 :             break;
     651             :         }
     652           0 :         case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer
     653             :         {
     654             :             using IntType = OddSizedInteger<3, false>;
     655           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
     656           0 :             break;
     657             :         }
     658           0 :         case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer
     659             :         {
     660           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<uint32_t>(*writer, isNullable));
     661           0 :             break;
     662             :         }
     663           0 :         case ZCL_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer
     664             :         {
     665             :             using IntType = OddSizedInteger<5, false>;
     666           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
     667           0 :             break;
     668             :         }
     669           0 :         case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer
     670             :         {
     671             :             using IntType = OddSizedInteger<6, false>;
     672           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
     673           0 :             break;
     674             :         }
     675           0 :         case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer
     676             :         {
     677             :             using IntType = OddSizedInteger<7, false>;
     678           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
     679           0 :             break;
     680             :         }
     681           0 :         case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer
     682             :         {
     683           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<uint64_t>(*writer, isNullable));
     684           0 :             break;
     685             :         }
     686           0 :         case ZCL_INT8S_ATTRIBUTE_TYPE: // Signed 8-bit integer
     687             :         {
     688           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<int8_t>(*writer, isNullable));
     689           0 :             break;
     690             :         }
     691           0 :         case ZCL_INT16S_ATTRIBUTE_TYPE: // Signed 16-bit integer
     692             :         {
     693           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<int16_t>(*writer, isNullable));
     694           0 :             break;
     695             :         }
     696           0 :         case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer
     697             :         {
     698             :             using IntType = OddSizedInteger<3, true>;
     699           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
     700           0 :             break;
     701             :         }
     702           0 :         case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer
     703             :         {
     704           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<int32_t>(*writer, isNullable));
     705           0 :             break;
     706             :         }
     707           0 :         case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer
     708             :         {
     709             :             using IntType = OddSizedInteger<5, true>;
     710           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
     711           0 :             break;
     712             :         }
     713           0 :         case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer
     714             :         {
     715             :             using IntType = OddSizedInteger<6, true>;
     716           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
     717           0 :             break;
     718             :         }
     719           0 :         case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer
     720             :         {
     721             :             using IntType = OddSizedInteger<7, true>;
     722           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<IntType>(*writer, isNullable));
     723           0 :             break;
     724             :         }
     725           0 :         case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer
     726             :         {
     727           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<int64_t>(*writer, isNullable));
     728           0 :             break;
     729             :         }
     730           0 :         case ZCL_SINGLE_ATTRIBUTE_TYPE: // 32-bit float
     731             :         {
     732           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<float>(*writer, isNullable));
     733           0 :             break;
     734             :         }
     735           0 :         case ZCL_DOUBLE_ATTRIBUTE_TYPE: // 64-bit float
     736             :         {
     737           0 :             ReturnErrorOnFailure(attributeBufferToNumericTlvData<double>(*writer, isNullable));
     738           0 :             break;
     739             :         }
     740           0 :         case ZCL_CHAR_STRING_ATTRIBUTE_TYPE: // Char string
     741             :         {
     742           0 :             char * actualData  = reinterpret_cast<char *>(attributeData + 1);
     743           0 :             uint8_t dataLength = attributeData[0];
     744           0 :             if (dataLength == 0xFF)
     745             :             {
     746           0 :                 if (isNullable)
     747             :                 {
     748           0 :                     ReturnErrorOnFailure(writer->PutNull(tag));
     749             :                 }
     750             :                 else
     751             :                 {
     752           0 :                     return CHIP_ERROR_INCORRECT_STATE;
     753             :                 }
     754             :             }
     755             :             else
     756             :             {
     757           0 :                 ReturnErrorOnFailure(writer->PutString(tag, actualData, dataLength));
     758             :             }
     759           0 :             break;
     760             :         }
     761           0 :         case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE: {
     762           0 :             char * actualData = reinterpret_cast<char *>(attributeData + 2); // The pascal string contains 2 bytes length
     763             :             uint16_t dataLength;
     764           0 :             memcpy(&dataLength, attributeData, sizeof(dataLength));
     765           0 :             if (dataLength == 0xFFFF)
     766             :             {
     767           0 :                 if (isNullable)
     768             :                 {
     769           0 :                     ReturnErrorOnFailure(writer->PutNull(tag));
     770             :                 }
     771             :                 else
     772             :                 {
     773           0 :                     return CHIP_ERROR_INCORRECT_STATE;
     774             :                 }
     775             :             }
     776             :             else
     777             :             {
     778           0 :                 ReturnErrorOnFailure(writer->PutString(tag, actualData, dataLength));
     779             :             }
     780           0 :             break;
     781             :         }
     782           0 :         case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string
     783             :         {
     784           0 :             uint8_t * actualData = attributeData + 1;
     785           0 :             uint8_t dataLength   = attributeData[0];
     786           0 :             if (dataLength == 0xFF)
     787             :             {
     788           0 :                 if (isNullable)
     789             :                 {
     790           0 :                     ReturnErrorOnFailure(writer->PutNull(tag));
     791             :                 }
     792             :                 else
     793             :                 {
     794           0 :                     return CHIP_ERROR_INCORRECT_STATE;
     795             :                 }
     796             :             }
     797             :             else
     798             :             {
     799           0 :                 ReturnErrorOnFailure(writer->Put(tag, chip::ByteSpan(actualData, dataLength)));
     800             :             }
     801           0 :             break;
     802             :         }
     803           0 :         case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE: {
     804           0 :             uint8_t * actualData = attributeData + 2; // The pascal string contains 2 bytes length
     805             :             uint16_t dataLength;
     806           0 :             memcpy(&dataLength, attributeData, sizeof(dataLength));
     807           0 :             if (dataLength == 0xFFFF)
     808             :             {
     809           0 :                 if (isNullable)
     810             :                 {
     811           0 :                     ReturnErrorOnFailure(writer->PutNull(tag));
     812             :                 }
     813             :                 else
     814             :                 {
     815           0 :                     return CHIP_ERROR_INCORRECT_STATE;
     816             :                 }
     817             :             }
     818             :             else
     819             :             {
     820           0 :                 ReturnErrorOnFailure(writer->Put(tag, chip::ByteSpan(actualData, dataLength)));
     821             :             }
     822           0 :             break;
     823             :         }
     824           0 :         default:
     825           0 :             ChipLogError(DataManagement, "Attribute type 0x%x not handled", static_cast<int>(attributeType));
     826           0 :             status = Status::UnsupportedRead;
     827             :         }
     828             :     }
     829             : 
     830           0 :     if (status == Protocols::InteractionModel::Status::Success)
     831             :     {
     832           0 :         return SendSuccessStatus(attributeReport, attributeDataIBBuilder);
     833             :     }
     834             : 
     835           0 :     return SendFailureStatus(aPath, aAttributeReports, status, &backup);
     836             : }
     837             : 
     838             : namespace {
     839             : 
     840             : template <typename T>
     841           0 : CHIP_ERROR numericTlvDataToAttributeBuffer(TLV::TLVReader & aReader, bool isNullable, uint16_t & dataLen)
     842             : {
     843             :     typename NumericAttributeTraits<T>::StorageType value;
     844             :     static_assert(sizeof(value) <= sizeof(attributeData), "Value cannot fit into attribute data");
     845           0 :     if (isNullable && aReader.GetType() == TLV::kTLVType_Null)
     846             :     {
     847           0 :         NumericAttributeTraits<T>::SetNull(value);
     848             :     }
     849             :     else
     850             :     {
     851             :         typename NumericAttributeTraits<T>::WorkingType val;
     852           0 :         ReturnErrorOnFailure(aReader.Get(val));
     853           0 :         VerifyOrReturnError(NumericAttributeTraits<T>::CanRepresentValue(isNullable, val), CHIP_ERROR_INVALID_ARGUMENT);
     854           0 :         NumericAttributeTraits<T>::WorkingToStorage(val, value);
     855             :     }
     856           0 :     dataLen = sizeof(value);
     857           0 :     memcpy(attributeData, &value, sizeof(value));
     858           0 :     return CHIP_NO_ERROR;
     859             : }
     860             : 
     861             : template <typename T>
     862           0 : CHIP_ERROR stringTlvDataToAttributeBuffer(TLV::TLVReader & aReader, bool isOctetString, bool isNullable, uint16_t & dataLen)
     863             : {
     864           0 :     const uint8_t * data = nullptr;
     865             :     T len;
     866           0 :     if (isNullable && aReader.GetType() == TLV::kTLVType_Null)
     867             :     {
     868             :         // Null is represented by an 0xFF or 0xFFFF length, respectively.
     869           0 :         len = std::numeric_limits<T>::max();
     870           0 :         memcpy(&attributeData[0], &len, sizeof(len));
     871           0 :         dataLen = sizeof(len);
     872             :     }
     873             :     else
     874             :     {
     875           0 :         VerifyOrReturnError((isOctetString && aReader.GetType() == TLV::TLVType::kTLVType_ByteString) ||
     876             :                                 (!isOctetString && aReader.GetType() == TLV::TLVType::kTLVType_UTF8String),
     877             :                             CHIP_ERROR_INVALID_ARGUMENT);
     878           0 :         VerifyOrReturnError(CanCastTo<T>(aReader.GetLength()), CHIP_ERROR_MESSAGE_TOO_LONG);
     879           0 :         ReturnErrorOnFailure(aReader.GetDataPtr(data));
     880           0 :         len = static_cast<T>(aReader.GetLength());
     881           0 :         VerifyOrReturnError(len != std::numeric_limits<T>::max(), CHIP_ERROR_MESSAGE_TOO_LONG);
     882           0 :         VerifyOrReturnError(len + sizeof(len) /* length at the beginning of data */ <= sizeof(attributeData),
     883             :                             CHIP_ERROR_MESSAGE_TOO_LONG);
     884           0 :         memcpy(&attributeData[0], &len, sizeof(len));
     885           0 :         memcpy(&attributeData[sizeof(len)], data, len);
     886           0 :         dataLen = static_cast<uint16_t>(len + sizeof(len));
     887             :     }
     888           0 :     return CHIP_NO_ERROR;
     889             : }
     890             : 
     891           0 : CHIP_ERROR prepareWriteData(const EmberAfAttributeMetadata * attributeMetadata, TLV::TLVReader & aReader, uint16_t & dataLen)
     892             : {
     893           0 :     EmberAfAttributeType expectedType = BaseType(attributeMetadata->attributeType);
     894           0 :     bool isNullable                   = attributeMetadata->IsNullable();
     895           0 :     switch (expectedType)
     896             :     {
     897           0 :     case ZCL_BOOLEAN_ATTRIBUTE_TYPE: // Boolean
     898           0 :         return numericTlvDataToAttributeBuffer<bool>(aReader, isNullable, dataLen);
     899           0 :     case ZCL_INT8U_ATTRIBUTE_TYPE: // Unsigned 8-bit integer
     900           0 :         return numericTlvDataToAttributeBuffer<uint8_t>(aReader, isNullable, dataLen);
     901           0 :     case ZCL_INT16U_ATTRIBUTE_TYPE: // Unsigned 16-bit integer
     902           0 :         return numericTlvDataToAttributeBuffer<uint16_t>(aReader, isNullable, dataLen);
     903           0 :     case ZCL_INT24U_ATTRIBUTE_TYPE: // Unsigned 24-bit integer
     904             :     {
     905             :         using IntType = OddSizedInteger<3, false>;
     906           0 :         return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
     907             :     }
     908           0 :     case ZCL_INT32U_ATTRIBUTE_TYPE: // Unsigned 32-bit integer
     909           0 :         return numericTlvDataToAttributeBuffer<uint32_t>(aReader, isNullable, dataLen);
     910           0 :     case ZCL_INT40U_ATTRIBUTE_TYPE: // Unsigned 40-bit integer
     911             :     {
     912             :         using IntType = OddSizedInteger<5, false>;
     913           0 :         return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
     914             :     }
     915           0 :     case ZCL_INT48U_ATTRIBUTE_TYPE: // Unsigned 48-bit integer
     916             :     {
     917             :         using IntType = OddSizedInteger<6, false>;
     918           0 :         return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
     919             :     }
     920           0 :     case ZCL_INT56U_ATTRIBUTE_TYPE: // Unsigned 56-bit integer
     921             :     {
     922             :         using IntType = OddSizedInteger<7, false>;
     923           0 :         return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
     924             :     }
     925           0 :     case ZCL_INT64U_ATTRIBUTE_TYPE: // Unsigned 64-bit integer
     926           0 :         return numericTlvDataToAttributeBuffer<uint64_t>(aReader, isNullable, dataLen);
     927           0 :     case ZCL_INT8S_ATTRIBUTE_TYPE: // Signed 8-bit integer
     928           0 :         return numericTlvDataToAttributeBuffer<int8_t>(aReader, isNullable, dataLen);
     929           0 :     case ZCL_INT16S_ATTRIBUTE_TYPE: // Signed 16-bit integer
     930           0 :         return numericTlvDataToAttributeBuffer<int16_t>(aReader, isNullable, dataLen);
     931           0 :     case ZCL_INT24S_ATTRIBUTE_TYPE: // Signed 24-bit integer
     932             :     {
     933             :         using IntType = OddSizedInteger<3, true>;
     934           0 :         return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
     935             :     }
     936           0 :     case ZCL_INT32S_ATTRIBUTE_TYPE: // Signed 32-bit integer
     937           0 :         return numericTlvDataToAttributeBuffer<int32_t>(aReader, isNullable, dataLen);
     938           0 :     case ZCL_INT40S_ATTRIBUTE_TYPE: // Signed 40-bit integer
     939             :     {
     940             :         using IntType = OddSizedInteger<5, true>;
     941           0 :         return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
     942             :     }
     943           0 :     case ZCL_INT48S_ATTRIBUTE_TYPE: // Signed 48-bit integer
     944             :     {
     945             :         using IntType = OddSizedInteger<6, true>;
     946           0 :         return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
     947             :     }
     948           0 :     case ZCL_INT56S_ATTRIBUTE_TYPE: // Signed 56-bit integer
     949             :     {
     950             :         using IntType = OddSizedInteger<7, true>;
     951           0 :         return numericTlvDataToAttributeBuffer<IntType>(aReader, isNullable, dataLen);
     952             :     }
     953           0 :     case ZCL_INT64S_ATTRIBUTE_TYPE: // Signed 64-bit integer
     954           0 :         return numericTlvDataToAttributeBuffer<int64_t>(aReader, isNullable, dataLen);
     955           0 :     case ZCL_SINGLE_ATTRIBUTE_TYPE: // 32-bit float
     956           0 :         return numericTlvDataToAttributeBuffer<float>(aReader, isNullable, dataLen);
     957           0 :     case ZCL_DOUBLE_ATTRIBUTE_TYPE: // 64-bit float
     958           0 :         return numericTlvDataToAttributeBuffer<double>(aReader, isNullable, dataLen);
     959           0 :     case ZCL_OCTET_STRING_ATTRIBUTE_TYPE: // Octet string
     960             :     case ZCL_CHAR_STRING_ATTRIBUTE_TYPE:  // Char string
     961           0 :         return stringTlvDataToAttributeBuffer<uint8_t>(aReader, expectedType == ZCL_OCTET_STRING_ATTRIBUTE_TYPE, isNullable,
     962           0 :                                                        dataLen);
     963           0 :     case ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE: // Long octet string
     964             :     case ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE:  // Long char string
     965           0 :         return stringTlvDataToAttributeBuffer<uint16_t>(aReader, expectedType == ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE, isNullable,
     966           0 :                                                         dataLen);
     967           0 :     default:
     968           0 :         ChipLogError(DataManagement, "Attribute type %x not handled", static_cast<int>(expectedType));
     969           0 :         return CHIP_ERROR_INVALID_DATA_LIST;
     970             :     }
     971             : }
     972             : } // namespace
     973             : 
     974        2440 : const EmberAfAttributeMetadata * GetAttributeMetadata(const ConcreteAttributePath & aPath)
     975             : {
     976        2440 :     return emberAfLocateAttributeMetadata(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId);
     977             : }
     978             : 
     979        2434 : CHIP_ERROR WriteSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, const ConcreteDataAttributePath & aPath,
     980             :                                   TLV::TLVReader & aReader, WriteHandler * apWriteHandler)
     981             : {
     982             :     // Check attribute existence. This includes attributes with registered metadata, but also specially handled
     983             :     // mandatory global attributes (which just check for cluster on endpoint).
     984        2434 :     const EmberAfCluster * attributeCluster            = nullptr;
     985        2434 :     const EmberAfAttributeMetadata * attributeMetadata = nullptr;
     986        2434 :     FindAttributeMetadata(aPath, &attributeCluster, &attributeMetadata);
     987             : 
     988        2434 :     if (attributeCluster == nullptr && attributeMetadata == nullptr)
     989             :     {
     990           0 :         return apWriteHandler->AddStatus(aPath, UnsupportedAttributeStatus(aPath));
     991             :     }
     992             : 
     993             :     // All the global attributes we don't have metadata for are readonly.
     994        2434 :     if (attributeMetadata == nullptr || attributeMetadata->IsReadOnly())
     995             :     {
     996           0 :         return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::UnsupportedWrite);
     997             :     }
     998             : 
     999             :     {
    1000        2434 :         Access::RequestPath requestPath{ .cluster = aPath.mClusterId, .endpoint = aPath.mEndpointId };
    1001        2434 :         Access::Privilege requestPrivilege = RequiredPrivilege::ForWriteAttribute(aPath);
    1002        2434 :         CHIP_ERROR err                     = CHIP_NO_ERROR;
    1003        2434 :         if (!apWriteHandler->ACLCheckCacheHit({ aPath, requestPrivilege }))
    1004             :         {
    1005         406 :             err = Access::GetAccessControl().Check(aSubjectDescriptor, requestPath, requestPrivilege);
    1006             :         }
    1007        2434 :         if (err != CHIP_NO_ERROR)
    1008             :         {
    1009           0 :             ReturnErrorCodeIf(err != CHIP_ERROR_ACCESS_DENIED, err);
    1010             :             // TODO: when wildcard/group writes are supported, handle them to discard rather than fail with status
    1011           0 :             return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::UnsupportedAccess);
    1012             :         }
    1013        2434 :         apWriteHandler->CacheACLCheckResult({ aPath, requestPrivilege });
    1014             :     }
    1015             : 
    1016        2434 :     if (attributeMetadata->MustUseTimedWrite() && !apWriteHandler->IsTimedWrite())
    1017             :     {
    1018           0 :         return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::NeedsTimedInteraction);
    1019             :     }
    1020             : 
    1021        2434 :     if (aPath.mDataVersion.HasValue() && !IsClusterDataVersionEqual(aPath, aPath.mDataVersion.Value()))
    1022             :     {
    1023           0 :         ChipLogError(DataManagement, "Write Version mismatch for Endpoint %x, Cluster " ChipLogFormatMEI, aPath.mEndpointId,
    1024             :                      ChipLogValueMEI(aPath.mClusterId));
    1025           0 :         return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::DataVersionMismatch);
    1026             :     }
    1027             : 
    1028        2434 :     if (auto * attrOverride = GetAttributeAccessOverride(aPath.mEndpointId, aPath.mClusterId))
    1029             :     {
    1030        2434 :         AttributeValueDecoder valueDecoder(aReader, aSubjectDescriptor);
    1031        2434 :         ReturnErrorOnFailure(attrOverride->Write(aPath, valueDecoder));
    1032             : 
    1033        2429 :         if (valueDecoder.TriedDecode())
    1034             :         {
    1035        2429 :             MatterReportingAttributeChangeCallback(aPath);
    1036        2429 :             return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::Success);
    1037             :         }
    1038             :     }
    1039             : 
    1040           0 :     CHIP_ERROR preparationError = CHIP_NO_ERROR;
    1041           0 :     uint16_t dataLen            = 0;
    1042           0 :     if ((preparationError = prepareWriteData(attributeMetadata, aReader, dataLen)) != CHIP_NO_ERROR)
    1043             :     {
    1044           0 :         ChipLogDetail(Zcl, "Failed to prepare data to write: %" CHIP_ERROR_FORMAT, preparationError.Format());
    1045           0 :         return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::InvalidValue);
    1046             :     }
    1047             : 
    1048           0 :     if (dataLen > attributeMetadata->size)
    1049             :     {
    1050           0 :         ChipLogDetail(Zcl, "Data to write exceedes the attribute size claimed.");
    1051           0 :         return apWriteHandler->AddStatus(aPath, Protocols::InteractionModel::Status::InvalidValue);
    1052             :     }
    1053             : 
    1054           0 :     auto status = emAfWriteAttributeExternal(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId, attributeData,
    1055           0 :                                              attributeMetadata->attributeType);
    1056           0 :     return apWriteHandler->AddStatus(aPath, status);
    1057             : }
    1058             : 
    1059           0 : bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath, DataVersion aRequiredVersion)
    1060             : {
    1061           0 :     DataVersion * version = emberAfDataVersionStorage(aConcreteClusterPath);
    1062           0 :     if (version == nullptr)
    1063             :     {
    1064           0 :         ChipLogError(DataManagement, "Endpoint %x, Cluster " ChipLogFormatMEI " not found in IsClusterDataVersionEqual!",
    1065             :                      aConcreteClusterPath.mEndpointId, ChipLogValueMEI(aConcreteClusterPath.mClusterId));
    1066           0 :         return false;
    1067             :     }
    1068             : 
    1069           0 :     return (*(version)) == aRequiredVersion;
    1070             : }
    1071             : 
    1072           0 : bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint)
    1073             : {
    1074             :     CHIP_ERROR err;
    1075           0 :     auto deviceTypeList = emberAfDeviceTypeListFromEndpoint(endpoint, err);
    1076           0 :     if (err != CHIP_NO_ERROR)
    1077             :     {
    1078           0 :         return false;
    1079             :     }
    1080             : 
    1081           0 :     for (auto & device : deviceTypeList)
    1082             :     {
    1083           0 :         if (device.deviceId == deviceType)
    1084             :         {
    1085           0 :             return true;
    1086             :         }
    1087             :     }
    1088             : 
    1089           0 :     return false;
    1090             : }
    1091             : 
    1092           0 : Protocols::InteractionModel::Status CheckEventSupportStatus(const ConcreteEventPath & aPath)
    1093             : {
    1094             :     using Protocols::InteractionModel::Status;
    1095             : 
    1096           0 :     const EmberAfEndpointType * type = emberAfFindEndpointType(aPath.mEndpointId);
    1097           0 :     if (type == nullptr)
    1098             :     {
    1099           0 :         return Status::UnsupportedEndpoint;
    1100             :     }
    1101             : 
    1102           0 :     const EmberAfCluster * cluster = emberAfFindClusterInType(type, aPath.mClusterId, CLUSTER_MASK_SERVER);
    1103           0 :     if (cluster == nullptr)
    1104             :     {
    1105           0 :         return Status::UnsupportedCluster;
    1106             :     }
    1107             : 
    1108             : #if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
    1109             :     for (size_t i = 0; i < cluster->eventCount; ++i)
    1110             :     {
    1111             :         if (cluster->eventList[i] == aPath.mEventId)
    1112             :         {
    1113             :             return Status::Success;
    1114             :         }
    1115             :     }
    1116             : 
    1117             :     return Status::UnsupportedEvent;
    1118             : #else
    1119             :     // No way to tell. Just claim supported.
    1120           0 :     return Status::Success;
    1121             : #endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
    1122             : }
    1123             : 
    1124             : } // namespace app
    1125             : } // namespace chip
    1126             : 
    1127        2476 : void MatterReportingAttributeChangeCallback(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId)
    1128             : {
    1129             :     // Attribute writes have asserted this already, but this assert should catch
    1130             :     // applications notifying about changes from their end.
    1131        2476 :     assertChipStackLockedByCurrentThread();
    1132             : 
    1133        2476 :     AttributePathParams info;
    1134        2476 :     info.mClusterId   = clusterId;
    1135        2476 :     info.mAttributeId = attributeId;
    1136        2476 :     info.mEndpointId  = endpoint;
    1137             : 
    1138        2476 :     IncreaseClusterDataVersion(ConcreteClusterPath(endpoint, clusterId));
    1139        2476 :     InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(info);
    1140        2476 : }
    1141             : 
    1142        2429 : void MatterReportingAttributeChangeCallback(const ConcreteAttributePath & aPath)
    1143             : {
    1144        2429 :     return MatterReportingAttributeChangeCallback(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId);
    1145             : }
    1146             : 
    1147          24 : void MatterReportingAttributeChangeCallback(EndpointId endpoint)
    1148             : {
    1149             :     // Attribute writes have asserted this already, but this assert should catch
    1150             :     // applications notifying about changes from their end.
    1151          24 :     assertChipStackLockedByCurrentThread();
    1152             : 
    1153          24 :     AttributePathParams info;
    1154          24 :     info.mEndpointId = endpoint;
    1155             : 
    1156             :     // We are adding or enabling a whole endpoint, in this case, we do not touch the cluster data version.
    1157             : 
    1158          24 :     InteractionModelEngine::GetInstance()->GetReportingEngine().SetDirty(info);
    1159          24 : }

Generated by: LCOV version 1.14