Matter SDK Coverage Report
Current view: top level - app - BufferedReadCallback.cpp (source / functions) Coverage Total Hit
Test: SHA:db08debc068562b264a2df3a7f3a8cc1d0b3aba1 Lines: 95.4 % 87 83
Test Date: 2025-10-02 07:10:30 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2021 Project CHIP Authors
       4              :  *    All rights reserved.
       5              :  *
       6              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7              :  *    you may not use this file except in compliance with the License.
       8              :  *    You may obtain a copy of the License at
       9              :  *
      10              :  *        http://www.apache.org/licenses/LICENSE-2.0
      11              :  *
      12              :  *    Unless required by applicable law or agreed to in writing, software
      13              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15              :  *    See the License for the specific language governing permissions and
      16              :  *    limitations under the License.
      17              :  */
      18              : 
      19              : #include "lib/core/TLV.h"
      20              : #include "lib/core/TLVTags.h"
      21              : #include "lib/core/TLVTypes.h"
      22              : #include "protocols/interaction_model/Constants.h"
      23              : #include "system/SystemPacketBuffer.h"
      24              : #include "system/TLVPacketBufferBackingStore.h"
      25              : #include <app/BufferedReadCallback.h>
      26              : #include <app/InteractionModelEngine.h>
      27              : #include <lib/support/ScopedBuffer.h>
      28              : 
      29              : namespace chip {
      30              : namespace app {
      31              : 
      32          823 : void BufferedReadCallback::OnReportBegin()
      33              : {
      34          823 :     mCallback.OnReportBegin();
      35          823 : }
      36              : 
      37          823 : void BufferedReadCallback::OnReportEnd()
      38              : {
      39          823 :     CHIP_ERROR err = DispatchBufferedData(mBufferedPath, StatusIB(), true);
      40          823 :     if (err != CHIP_NO_ERROR)
      41              :     {
      42            0 :         mCallback.OnError(err);
      43            0 :         return;
      44              :     }
      45              : 
      46          823 :     mCallback.OnReportEnd();
      47              : }
      48              : 
      49         1126 : CHIP_ERROR BufferedReadCallback::GenerateListTLV(TLV::ScopedBufferTLVReader & aReader)
      50              : {
      51              :     TLV::TLVType outerType;
      52         1126 :     Platform::ScopedMemoryBuffer<uint8_t> backingBuffer;
      53              : 
      54              :     //
      55              :     // To generate the final reconstituted list, we need to allocate a contiguous
      56              :     // buffer than can hold the entirety of its contents. To do so, we need to figure out
      57              :     // how big a buffer to allocate. This requires walking the buffered list items and computing their TLV sizes,
      58              :     // summing them all up and adding a bit of slop to account for the TLV array the list elements will go into.
      59              :     //
      60              :     // The alternative was to use a PacketBufferTLVWriter backed by chained packet buffers to
      61              :     // write out the list - this would have removed the need for this first pass. However,
      62              :     // we cannot actually back a TLVReader with a chained buffer since that violates the ability
      63              :     // for us to create readers off-of readers. Each reader would assume exclusive ownership of the chained
      64              :     // buffer and mutate the state within TLVPacketBufferBackingStore, preventing shared use.
      65              :     //
      66              :     // To avoid that, a single contiguous buffer is the best likely approach for now.
      67              :     //
      68         1126 :     size_t totalBufSize = 0;
      69         7524 :     for (const auto & packetBuffer : mBufferedList)
      70              :     {
      71         6398 :         totalBufSize += packetBuffer->TotalLength();
      72              :     }
      73              : 
      74              :     //
      75              :     // Size of the start container and end container are just 1 byte each, but, let's just be safe.
      76              :     //
      77         1126 :     totalBufSize += 4;
      78              : 
      79         1126 :     backingBuffer.Calloc(totalBufSize);
      80         1126 :     VerifyOrReturnError(backingBuffer.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
      81              : 
      82         1126 :     TLV::ScopedBufferTLVWriter writer(std::move(backingBuffer), totalBufSize);
      83         1126 :     ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, outerType));
      84              : 
      85         7524 :     for (auto & bufHandle : mBufferedList)
      86              :     {
      87         6398 :         System::PacketBufferTLVReader reader;
      88              : 
      89         6398 :         reader.Init(std::move(bufHandle));
      90              : 
      91         6398 :         ReturnErrorOnFailure(reader.Next());
      92         6398 :         ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), reader));
      93         6398 :     }
      94              : 
      95         1126 :     ReturnErrorOnFailure(writer.EndContainer(outerType));
      96              : 
      97         1126 :     writer.Finalize(backingBuffer);
      98              : 
      99         1126 :     aReader.Init(std::move(backingBuffer), totalBufSize);
     100              : 
     101         1126 :     return CHIP_NO_ERROR;
     102         1126 : }
     103              : 
     104         8012 : CHIP_ERROR BufferedReadCallback::BufferListItem(TLV::TLVReader & reader)
     105              : {
     106         8012 :     System::PacketBufferTLVWriter writer;
     107         8012 :     System::PacketBufferHandle handle;
     108              : 
     109              :     //
     110              :     // We conservatively allocate a packet buffer as big as an IPv6 MTU (since we're buffering
     111              :     // data received over the wire, which should always fit within that).
     112              :     //
     113              :     // We could have snapshotted the reader at its current position, advanced it past the current element
     114              :     // and computed the delta in its read point to figure out the size of the element before allocating
     115              :     // our target buffer. However, the reader's current position is already set past the control octet
     116              :     // and the tag. Consequently, the computed size is always going to omit the sizes of these two parts of the
     117              :     // TLV element. Since the tag can vary in size, for now, let's just do the safe thing. In the future, if this is a problem,
     118              :     // we can improve this.
     119              :     //
     120         8012 :     const size_t bufSize = mAllowLargePayload ? chip::app::kMaxLargeSecureSduLengthBytes : chip::app::kMaxSecureSduLengthBytes;
     121         8012 :     handle               = System::PacketBufferHandle::New(bufSize);
     122         8012 :     VerifyOrReturnError(!handle.IsNull(), CHIP_ERROR_NO_MEMORY);
     123              : 
     124         8012 :     writer.Init(std::move(handle), false);
     125              : 
     126         8012 :     ReturnErrorOnFailure(writer.CopyElement(TLV::AnonymousTag(), reader));
     127         8012 :     ReturnErrorOnFailure(writer.Finalize(&handle));
     128              : 
     129              :     // Compact the buffer down to a more reasonably sized packet buffer
     130              :     // if we can.
     131              :     //
     132         8012 :     handle.RightSize();
     133              : 
     134         8012 :     mBufferedList.push_back(std::move(handle));
     135              : 
     136         8012 :     return CHIP_NO_ERROR;
     137         8012 : }
     138              : 
     139         3558 : CHIP_ERROR BufferedReadCallback::BufferData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData)
     140              : {
     141              : 
     142         3558 :     if (aPath.mListOp == ConcreteDataAttributePath::ListOperation::ReplaceAll)
     143              :     {
     144              :         TLV::TLVType outerContainer;
     145              : 
     146         1271 :         VerifyOrReturnError(apData->GetType() == TLV::kTLVType_Array, CHIP_ERROR_INVALID_TLV_ELEMENT);
     147         1271 :         mBufferedList.clear();
     148              : 
     149         1271 :         ReturnErrorOnFailure(apData->EnterContainer(outerContainer));
     150              : 
     151              :         CHIP_ERROR err;
     152              : 
     153         6996 :         while ((err = apData->Next()) == CHIP_NO_ERROR)
     154              :         {
     155         5725 :             ReturnErrorOnFailure(BufferListItem(*apData));
     156              :         }
     157              : 
     158         1271 :         if (err == CHIP_END_OF_TLV)
     159              :         {
     160         1271 :             err = CHIP_NO_ERROR;
     161              :         }
     162              : 
     163         1271 :         ReturnErrorOnFailure(err);
     164         1271 :         ReturnErrorOnFailure(apData->ExitContainer(outerContainer));
     165              :     }
     166         2287 :     else if (aPath.mListOp == ConcreteDataAttributePath::ListOperation::AppendItem)
     167              :     {
     168         2287 :         ReturnErrorOnFailure(BufferListItem(*apData));
     169              :     }
     170              : 
     171         3558 :     return CHIP_NO_ERROR;
     172              : }
     173              : 
     174         5731 : CHIP_ERROR BufferedReadCallback::DispatchBufferedData(const ConcreteAttributePath & aPath, const StatusIB & aStatusIB,
     175              :                                                       bool aEndOfReport)
     176              : {
     177         5731 :     if (aPath == mBufferedPath)
     178              :     {
     179              :         //
     180              :         // If we encountered the same list again and it's not the last DataIB, then
     181              :         // we need to continue to buffer up this list's data, so return immediately without dispatching
     182              :         // the existing buffered up contents.
     183              :         //
     184         3268 :         if (!aEndOfReport)
     185              :         {
     186         2445 :             return CHIP_NO_ERROR;
     187              :         }
     188              : 
     189              :         //
     190              :         // If we had previously buffered up data for this list and now we have encountered
     191              :         // an error for this list, that error takes precedence and the buffered data is now
     192              :         // rendered invalid. Return immediately without dispatching the existing buffered up contents.
     193              :         //
     194          823 :         if (aStatusIB.mStatus != Protocols::InteractionModel::Status::Success)
     195              :         {
     196            0 :             return CHIP_NO_ERROR;
     197              :         }
     198              :     }
     199              : 
     200         3286 :     if (!mBufferedPath.IsListOperation())
     201              :     {
     202         2160 :         return CHIP_NO_ERROR;
     203              :     }
     204              : 
     205         1126 :     StatusIB statusIB;
     206         1126 :     TLV::ScopedBufferTLVReader reader;
     207              : 
     208         1126 :     ReturnErrorOnFailure(GenerateListTLV(reader));
     209              : 
     210              :     //
     211              :     // Update the list operation to now reflect the delivery of the entire list
     212              :     // i.e a replace all operation.
     213              :     //
     214         1126 :     mBufferedPath.mListOp = ConcreteDataAttributePath::ListOperation::ReplaceAll;
     215              : 
     216              :     //
     217              :     // Advance the reader forward to the list itself
     218              :     //
     219         1126 :     ReturnErrorOnFailure(reader.Next());
     220              : 
     221         1126 :     mCallback.OnAttributeData(mBufferedPath, &reader, statusIB);
     222              : 
     223              :     //
     224              :     // Clear out our buffered contents to free up allocated buffers, and reset the buffered path.
     225              :     //
     226         1126 :     mBufferedList.clear();
     227         1126 :     mBufferedPath = ConcreteDataAttributePath();
     228         1126 :     return CHIP_NO_ERROR;
     229         1126 : }
     230              : 
     231         4908 : void BufferedReadCallback::OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData,
     232              :                                            const StatusIB & aStatus)
     233              : {
     234              :     CHIP_ERROR err;
     235              : 
     236              :     //
     237              :     // First, let's dispatch to our registered callback any buffered up list data from previous calls.
     238              :     //
     239         4908 :     err = DispatchBufferedData(aPath, aStatus);
     240         4908 :     SuccessOrExit(err);
     241              : 
     242              :     //
     243              :     // We buffer up list data (only if the status was successful)
     244              :     //
     245         4908 :     if (aPath.IsListOperation() && aStatus.mStatus == Protocols::InteractionModel::Status::Success)
     246              :     {
     247         3558 :         err = BufferData(aPath, apData);
     248         3558 :         SuccessOrExit(err);
     249              :     }
     250              :     else
     251              :     {
     252         1350 :         mCallback.OnAttributeData(aPath, apData, aStatus);
     253              :     }
     254              : 
     255              :     //
     256              :     // Update our latched buffered path.
     257              :     //
     258         4908 :     mBufferedPath = aPath;
     259              : 
     260         4908 : exit:
     261         4908 :     if (err != CHIP_NO_ERROR)
     262              :     {
     263            0 :         mCallback.OnError(err);
     264              :     }
     265         4908 : }
     266              : 
     267              : } // namespace app
     268              : } // namespace chip
        

Generated by: LCOV version 2.0-1