Matter SDK Coverage Report
Current view: top level - data-model-providers/codegen - CodegenDataModelProvider_Write.cpp (source / functions) Coverage Total Hit
Test: SHA:f84fe08d06f240e801b5d923f8a938a9938ca110 Lines: 84.2 % 57 48
Test Date: 2025-02-22 08:08:07 Functions: 80.0 % 5 4

            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         2582 : DataModel::ActionReturnStatus CodegenDataModelProvider::WriteAttribute(const DataModel::WriteAttributeRequest & request,
      93              :                                                                        AttributeValueDecoder & decoder)
      94              : {
      95         2582 :     auto metadata = Ember::FindAttributeMetadata(request.path);
      96              : 
      97              :     // Explicit failure in finding a suitable metadata
      98         2582 :     if (const Status * status = std::get_if<Status>(&metadata))
      99              :     {
     100            4 :         VerifyOrDie((*status == Status::UnsupportedEndpoint) || //
     101              :                     (*status == Status::UnsupportedCluster) ||  //
     102              :                     (*status == Status::UnsupportedAttribute));
     103              : 
     104              :         // Check if this is an attribute that ember does not know about but is valid after all and
     105              :         // adjust the return code. All these global attributes are `read only` hence the return
     106              :         // of unsupported write.
     107              :         //
     108              :         // If the cluster or endpoint does not exist, though, keep that return code.
     109            6 :         if ((*status == Protocols::InteractionModel::Status::UnsupportedAttribute) &&
     110            2 :             IsSupportedGlobalAttributeNotInMetadata(request.path.mAttributeId))
     111              :         {
     112            1 :             return Status::UnsupportedWrite;
     113              :         }
     114              : 
     115            3 :         return *status;
     116              :     }
     117              : 
     118         2578 :     const EmberAfAttributeMetadata ** attributeMetadata = std::get_if<const EmberAfAttributeMetadata *>(&metadata);
     119         2578 :     VerifyOrDie(*attributeMetadata != nullptr);
     120              : 
     121              :     // Extra check: internal requests can bypass the read only check, however global attributes
     122              :     // have no underlying storage, so write still cannot be done
     123              :     //
     124              :     // I.e. if we get a `EmberAfCluster*` value from finding metadata, we fail here.
     125         2578 :     VerifyOrReturnError(attributeMetadata != nullptr, Status::UnsupportedWrite);
     126              : 
     127         2578 :     if (request.path.mDataVersion.HasValue())
     128              :     {
     129            2 :         DataVersion * versionPtr = emberAfDataVersionStorage(request.path);
     130              : 
     131            2 :         if (versionPtr == nullptr)
     132              :         {
     133            0 :             ChipLogError(DataManagement, "Unable to get cluster info for Endpoint 0x%x, Cluster " ChipLogFormatMEI,
     134              :                          request.path.mEndpointId, ChipLogValueMEI(request.path.mClusterId));
     135            0 :             return Status::DataVersionMismatch;
     136              :         }
     137              : 
     138            2 :         if (request.path.mDataVersion.Value() != *versionPtr)
     139              :         {
     140            1 :             ChipLogError(DataManagement, "Write Version mismatch for Endpoint 0x%x, Cluster " ChipLogFormatMEI,
     141              :                          request.path.mEndpointId, ChipLogValueMEI(request.path.mClusterId));
     142            1 :             return Status::DataVersionMismatch;
     143              :         }
     144              :     }
     145              : 
     146         2577 :     ContextAttributesChangeListener change_listener(CurrentContext());
     147              : 
     148              :     AttributeAccessInterface * aai =
     149         2577 :         AttributeAccessInterfaceRegistry::Instance().Get(request.path.mEndpointId, request.path.mClusterId);
     150         2577 :     std::optional<CHIP_ERROR> aai_result = TryWriteViaAccessInterface(request.path, aai, decoder);
     151         2577 :     if (aai_result.has_value())
     152              :     {
     153         2436 :         if (*aai_result == CHIP_NO_ERROR)
     154              :         {
     155              :             // TODO: this is awkward since it provides AAI no control over this, specifically
     156              :             //       AAI may not want to increase versions for some attributes that are Q
     157         2430 :             emberAfAttributeChanged(request.path.mEndpointId, request.path.mClusterId, request.path.mAttributeId, &change_listener);
     158              :         }
     159         2436 :         return *aai_result;
     160              :     }
     161              : 
     162          141 :     MutableByteSpan dataBuffer = gEmberAttributeIOBufferSpan;
     163              :     {
     164          141 :         Ember::EmberAttributeDataBuffer emberData(*attributeMetadata, dataBuffer);
     165          141 :         ReturnErrorOnFailure(decoder.Decode(emberData));
     166              :     }
     167              : 
     168              :     Protocols::InteractionModel::Status status;
     169              : 
     170          117 :     if (dataBuffer.size() > (*attributeMetadata)->size)
     171              :     {
     172            1 :         ChipLogDetail(Zcl, "Data to write exceeds the attribute size claimed.");
     173            1 :         return Status::InvalidValue;
     174              :     }
     175              : 
     176          116 :     EmberAfWriteDataInput dataInput(dataBuffer.data(), (*attributeMetadata)->attributeType);
     177              : 
     178          116 :     dataInput.SetChangeListener(&change_listener);
     179              :     // TODO: dataInput.SetMarkDirty() should be according to `ChangesOmmited`
     180              : 
     181          116 :     if (request.operationFlags.Has(DataModel::OperationFlags::kInternal))
     182              :     {
     183              :         // Internal requests use the non-External interface that has less enforcement
     184              :         // than the external version (e.g. does not check/enforce writable settings, does not
     185              :         // validate attribute types) - see attribute-table.h documentation for details.
     186            0 :         status = emberAfWriteAttribute(request.path, dataInput);
     187              :     }
     188              :     else
     189              :     {
     190          116 :         status = emAfWriteAttributeExternal(request.path, dataInput);
     191              :     }
     192              : 
     193          116 :     if (status != Protocols::InteractionModel::Status::Success)
     194              :     {
     195            2 :         return status;
     196              :     }
     197              : 
     198          114 :     return CHIP_NO_ERROR;
     199         2577 : }
     200              : 
     201            0 : void CodegenDataModelProvider::Temporary_ReportAttributeChanged(const AttributePathParams & path)
     202              : {
     203            0 :     ContextAttributesChangeListener change_listener(CurrentContext());
     204            0 :     if (path.mClusterId != kInvalidClusterId)
     205              :     {
     206            0 :         emberAfAttributeChanged(path.mEndpointId, path.mClusterId, path.mAttributeId, &change_listener);
     207              :     }
     208              :     else
     209              :     {
     210              :         // When the path has wildcard cluster Id, call the emberAfEndpointChanged to mark attributes on the given endpoint
     211              :         // as having changing, but do NOT increase/alter any cluster data versions, as this happens when a bridged endpoint is
     212              :         // added or removed from a bridge and the cluster data is not changed during the process.
     213            0 :         emberAfEndpointChanged(path.mEndpointId, &change_listener);
     214              :     }
     215            0 : }
     216              : 
     217              : } // namespace app
     218              : } // namespace chip
        

Generated by: LCOV version 2.0-1