Matter SDK Coverage Report
Current view: top level - data-model-providers/codegen - CodegenDataModelProvider_Write.cpp (source / functions) Coverage Total Hit
Test: SHA:1560a87972ec2c7a76cec101927a563a6862bc2a Lines: 85.3 % 75 64
Test Date: 2025-03-30 07:08:27 Functions: 83.3 % 6 5

            Line data    Source code
       1              : /*
       2              :  *    Copyright (c) 2024 Project CHIP Authors
       3              :  *    All rights reserved.
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : #include <data-model-providers/codegen/CodegenDataModelProvider.h>
      18              : 
      19              : #include <access/AccessControl.h>
      20              : #include <app-common/zap-generated/attribute-type.h>
      21              : #include <app/AttributeAccessInterface.h>
      22              : #include <app/AttributeAccessInterfaceRegistry.h>
      23              : #include <app/GlobalAttributes.h>
      24              : #include <app/RequiredPrivilege.h>
      25              : #include <app/data-model/FabricScoped.h>
      26              : #include <app/reporting/reporting.h>
      27              : #include <app/util/af-types.h>
      28              : #include <app/util/attribute-metadata.h>
      29              : #include <app/util/attribute-storage-detail.h>
      30              : #include <app/util/attribute-storage-null-handling.h>
      31              : #include <app/util/attribute-storage.h>
      32              : #include <app/util/attribute-table-detail.h>
      33              : #include <app/util/attribute-table.h>
      34              : #include <app/util/ember-io-storage.h>
      35              : #include <app/util/ember-strings.h>
      36              : #include <app/util/odd-sized-integers.h>
      37              : #include <data-model-providers/codegen/EmberAttributeDataBuffer.h>
      38              : #include <data-model-providers/codegen/EmberMetadata.h>
      39              : #include <lib/core/CHIPError.h>
      40              : #include <lib/support/CodeUtils.h>
      41              : 
      42              : #include <zap-generated/endpoint_config.h>
      43              : 
      44              : namespace chip {
      45              : namespace app {
      46              : namespace {
      47              : 
      48              : using namespace chip::app::Compatibility::Internal;
      49              : using Protocols::InteractionModel::Status;
      50              : 
      51              : class ContextAttributesChangeListener : public AttributesChangedListener
      52              : {
      53              : public:
      54         2577 :     ContextAttributesChangeListener(const DataModel::InteractionModelContext & context) : mListener(context.dataModelChangeListener)
      55         2577 :     {}
      56         2544 :     void MarkDirty(const AttributePathParams & path) override { mListener->MarkDirty(path); }
      57              : 
      58              : private:
      59              :     DataModel::ProviderChangeListener * mListener;
      60              : };
      61              : 
      62              : /// Attempts to write via an attribute access interface (AAI)
      63              : ///
      64              : /// If it returns a CHIP_ERROR, then this is a FINAL result (i.e. either failure or success)
      65              : ///
      66              : /// If it returns std::nullopt, then there is no AAI to handle the given path
      67              : /// and processing should figure out the value otherwise (generally from other ember data)
      68         2577 : std::optional<CHIP_ERROR> TryWriteViaAccessInterface(const ConcreteDataAttributePath & path, AttributeAccessInterface * aai,
      69              :                                                      AttributeValueDecoder & decoder)
      70              : {
      71              :     // Processing can happen only if an attribute access interface actually exists..
      72         2577 :     if (aai == nullptr)
      73              :     {
      74          137 :         return std::nullopt;
      75              :     }
      76              : 
      77         2440 :     CHIP_ERROR err = aai->Write(path, decoder);
      78              : 
      79         2440 :     if (err != CHIP_NO_ERROR)
      80              :     {
      81            6 :         return std::make_optional(err);
      82              :     }
      83              : 
      84              :     // If the decoder tried to decode, then a value should have been read for processing.
      85              :     //   - if decoding was done, assume DONE (i.e. final CHIP_NO_ERROR)
      86              :     //   -  otherwise, if no decoding done, return that processing must continue via nullopt
      87         4868 :     return decoder.TriedDecode() ? std::make_optional(CHIP_NO_ERROR) : std::nullopt;
      88              : }
      89              : 
      90              : } // namespace
      91              : 
      92         2583 : DataModel::ActionReturnStatus CodegenDataModelProvider::WriteAttribute(const DataModel::WriteAttributeRequest & request,
      93              :                                                                        AttributeValueDecoder & decoder)
      94              : {
      95         2583 :     if (auto * cluster = mRegistry.Get(request.path); cluster != nullptr)
      96              :     {
      97            1 :         return cluster->WriteAttribute(request, decoder);
      98              :     }
      99              : 
     100         2582 :     auto metadata = Ember::FindAttributeMetadata(request.path);
     101              : 
     102              :     // Explicit failure in finding a suitable metadata
     103         2582 :     if (const Status * status = std::get_if<Status>(&metadata))
     104              :     {
     105            4 :         VerifyOrDie((*status == Status::UnsupportedEndpoint) || //
     106              :                     (*status == Status::UnsupportedCluster) ||  //
     107              :                     (*status == Status::UnsupportedAttribute));
     108              : 
     109              :         // Check if this is an attribute that ember does not know about but is valid after all and
     110              :         // adjust the return code. All these global attributes are `read only` hence the return
     111              :         // of unsupported write.
     112              :         //
     113              :         // If the cluster or endpoint does not exist, though, keep that return code.
     114            6 :         if ((*status == Protocols::InteractionModel::Status::UnsupportedAttribute) &&
     115            2 :             IsSupportedGlobalAttributeNotInMetadata(request.path.mAttributeId))
     116              :         {
     117            1 :             return Status::UnsupportedWrite;
     118              :         }
     119              : 
     120            3 :         return *status;
     121              :     }
     122              : 
     123         2578 :     const EmberAfAttributeMetadata ** attributeMetadata = std::get_if<const EmberAfAttributeMetadata *>(&metadata);
     124         2578 :     VerifyOrDie(*attributeMetadata != nullptr);
     125              : 
     126              :     // Extra check: internal requests can bypass the read only check, however global attributes
     127              :     // have no underlying storage, so write still cannot be done
     128              :     //
     129              :     // I.e. if we get a `EmberAfCluster*` value from finding metadata, we fail here.
     130         2578 :     VerifyOrReturnError(attributeMetadata != nullptr, Status::UnsupportedWrite);
     131              : 
     132         2578 :     if (request.path.mDataVersion.HasValue())
     133              :     {
     134            2 :         DataVersion * versionPtr = emberAfDataVersionStorage(request.path);
     135              : 
     136            2 :         if (versionPtr == nullptr)
     137              :         {
     138            0 :             ChipLogError(DataManagement, "Unable to get cluster info for Endpoint 0x%x, Cluster " ChipLogFormatMEI,
     139              :                          request.path.mEndpointId, ChipLogValueMEI(request.path.mClusterId));
     140            0 :             return Status::DataVersionMismatch;
     141              :         }
     142              : 
     143            2 :         if (request.path.mDataVersion.Value() != *versionPtr)
     144              :         {
     145            1 :             ChipLogError(DataManagement, "Write Version mismatch for Endpoint 0x%x, Cluster " ChipLogFormatMEI,
     146              :                          request.path.mEndpointId, ChipLogValueMEI(request.path.mClusterId));
     147            1 :             return Status::DataVersionMismatch;
     148              :         }
     149              :     }
     150              : 
     151         2577 :     ContextAttributesChangeListener change_listener(CurrentContext());
     152              : 
     153              :     AttributeAccessInterface * aai =
     154         2577 :         AttributeAccessInterfaceRegistry::Instance().Get(request.path.mEndpointId, request.path.mClusterId);
     155         2577 :     std::optional<CHIP_ERROR> aai_result = TryWriteViaAccessInterface(request.path, aai, decoder);
     156         2577 :     if (aai_result.has_value())
     157              :     {
     158         2436 :         if (*aai_result == CHIP_NO_ERROR)
     159              :         {
     160              :             // TODO: this is awkward since it provides AAI no control over this, specifically
     161              :             //       AAI may not want to increase versions for some attributes that are Q
     162         2430 :             emberAfAttributeChanged(request.path.mEndpointId, request.path.mClusterId, request.path.mAttributeId, &change_listener);
     163              :         }
     164         2436 :         return *aai_result;
     165              :     }
     166              : 
     167          141 :     MutableByteSpan dataBuffer = gEmberAttributeIOBufferSpan;
     168              :     {
     169          141 :         Ember::EmberAttributeDataBuffer emberData(*attributeMetadata, dataBuffer);
     170          141 :         ReturnErrorOnFailure(decoder.Decode(emberData));
     171              :     }
     172              : 
     173              :     Protocols::InteractionModel::Status status;
     174              : 
     175          117 :     if (dataBuffer.size() > (*attributeMetadata)->size)
     176              :     {
     177            1 :         ChipLogDetail(Zcl, "Data to write exceeds the attribute size claimed.");
     178            1 :         return Status::InvalidValue;
     179              :     }
     180              : 
     181          116 :     EmberAfWriteDataInput dataInput(dataBuffer.data(), (*attributeMetadata)->attributeType);
     182              : 
     183          116 :     dataInput.SetChangeListener(&change_listener);
     184              :     // TODO: dataInput.SetMarkDirty() should be according to `ChangesOmmited`
     185              : 
     186          116 :     if (request.operationFlags.Has(DataModel::OperationFlags::kInternal))
     187              :     {
     188              :         // Internal requests use the non-External interface that has less enforcement
     189              :         // than the external version (e.g. does not check/enforce writable settings, does not
     190              :         // validate attribute types) - see attribute-table.h documentation for details.
     191            0 :         status = emberAfWriteAttribute(request.path, dataInput);
     192              :     }
     193              :     else
     194              :     {
     195          116 :         status = emAfWriteAttributeExternal(request.path, dataInput);
     196              :     }
     197              : 
     198          116 :     if (status != Protocols::InteractionModel::Status::Success)
     199              :     {
     200            2 :         return status;
     201              :     }
     202              : 
     203          114 :     return CHIP_NO_ERROR;
     204         2577 : }
     205              : 
     206          819 : void CodegenDataModelProvider::ListAttributeWriteNotification(const ConcreteAttributePath & aPath,
     207              :                                                               DataModel::ListWriteOperation opType)
     208              : {
     209          819 :     if (auto * cluster = mRegistry.Get(aPath); cluster != nullptr)
     210              :     {
     211            0 :         cluster->ListAttributeWriteNotification(aPath, opType);
     212            0 :         return;
     213              :     }
     214              : 
     215          819 :     AttributeAccessInterface * aai = AttributeAccessInterfaceRegistry::Instance().Get(aPath.mEndpointId, aPath.mClusterId);
     216              : 
     217          819 :     if (aai != nullptr)
     218              :     {
     219          812 :         switch (opType)
     220              :         {
     221          406 :         case DataModel::ListWriteOperation::kListWriteBegin:
     222          406 :             aai->OnListWriteBegin(aPath);
     223          406 :             break;
     224            3 :         case DataModel::ListWriteOperation::kListWriteFailure:
     225            3 :             aai->OnListWriteEnd(aPath, false);
     226            3 :             break;
     227          403 :         case DataModel::ListWriteOperation::kListWriteSuccess:
     228          403 :             aai->OnListWriteEnd(aPath, true);
     229          403 :             break;
     230              :         }
     231              :     }
     232              : }
     233              : 
     234            0 : void CodegenDataModelProvider::Temporary_ReportAttributeChanged(const AttributePathParams & path)
     235              : {
     236            0 :     ContextAttributesChangeListener change_listener(CurrentContext());
     237            0 :     if (path.mClusterId != kInvalidClusterId)
     238              :     {
     239            0 :         emberAfAttributeChanged(path.mEndpointId, path.mClusterId, path.mAttributeId, &change_listener);
     240              :     }
     241              :     else
     242              :     {
     243              :         // When the path has wildcard cluster Id, call the emberAfEndpointChanged to mark attributes on the given endpoint
     244              :         // as having changing, but do NOT increase/alter any cluster data versions, as this happens when a bridged endpoint is
     245              :         // added or removed from a bridge and the cluster data is not changed during the process.
     246            0 :         emberAfEndpointChanged(path.mEndpointId, &change_listener);
     247              :     }
     248            0 : }
     249              : 
     250              : } // namespace app
     251              : } // namespace chip
        

Generated by: LCOV version 2.0-1