LCOV - code coverage report
Current view: top level - app - WriteClient.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 185 243 76.1 %
Date: 2024-02-15 08:20:41 Functions: 15 18 83.3 %

          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             : /**
      20             :  *    @file
      21             :  *      This file defines the initiator side of a CHIP Write Interaction.
      22             :  *
      23             :  */
      24             : 
      25             : #include "lib/core/CHIPError.h"
      26             : #include <app/AppConfig.h>
      27             : #include <app/InteractionModelEngine.h>
      28             : #include <app/TimedRequest.h>
      29             : #include <app/WriteClient.h>
      30             : 
      31             : namespace chip {
      32             : namespace app {
      33             : 
      34         427 : void WriteClient::Close()
      35             : {
      36         427 :     MoveToState(State::AwaitingDestruction);
      37             : 
      38         427 :     if (mpCallback)
      39             :     {
      40         427 :         mpCallback->OnDone(this);
      41             :     }
      42         427 : }
      43             : 
      44        1727 : CHIP_ERROR WriteClient::ProcessWriteResponseMessage(System::PacketBufferHandle && payload)
      45             : {
      46        1727 :     CHIP_ERROR err = CHIP_NO_ERROR;
      47        1727 :     System::PacketBufferTLVReader reader;
      48             :     TLV::TLVReader attributeStatusesReader;
      49        1727 :     WriteResponseMessage::Parser writeResponse;
      50        1727 :     AttributeStatusIBs::Parser attributeStatusesParser;
      51             : 
      52        1727 :     reader.Init(std::move(payload));
      53             : 
      54        1727 :     ReturnErrorOnFailure(writeResponse.Init(reader));
      55             : 
      56             : #if CHIP_CONFIG_IM_PRETTY_PRINT
      57        1726 :     writeResponse.PrettyPrint();
      58             : #endif
      59             : 
      60        1726 :     err = writeResponse.GetWriteResponses(&attributeStatusesParser);
      61        1726 :     if (err == CHIP_END_OF_TLV)
      62             :     {
      63           0 :         return CHIP_NO_ERROR;
      64             :     }
      65        1726 :     ReturnErrorOnFailure(err);
      66             : 
      67        1726 :     attributeStatusesParser.GetReader(&attributeStatusesReader);
      68             : 
      69        4207 :     while (CHIP_NO_ERROR == (err = attributeStatusesReader.Next()))
      70             :     {
      71        2481 :         VerifyOrReturnError(TLV::AnonymousTag() == attributeStatusesReader.GetTag(), err = CHIP_ERROR_INVALID_TLV_TAG);
      72             : 
      73        2481 :         AttributeStatusIB::Parser element;
      74             : 
      75        2481 :         ReturnErrorOnFailure(element.Init(attributeStatusesReader));
      76        2481 :         ReturnErrorOnFailure(ProcessAttributeStatusIB(element));
      77             :     }
      78             : 
      79             :     // if we have exhausted this container
      80        1726 :     if (CHIP_END_OF_TLV == err)
      81             :     {
      82        1726 :         err = CHIP_NO_ERROR;
      83             :     }
      84        1726 :     ReturnErrorOnFailure(err);
      85        1726 :     return writeResponse.ExitContainer();
      86        1727 : }
      87             : 
      88        3963 : CHIP_ERROR WriteClient::PrepareAttributeIB(const ConcreteDataAttributePath & aPath)
      89             : {
      90        3963 :     AttributeDataIBs::Builder & writeRequests  = mWriteRequestBuilder.GetWriteRequests();
      91        3963 :     AttributeDataIB::Builder & attributeDataIB = writeRequests.CreateAttributeDataIBBuilder();
      92        3963 :     ReturnErrorOnFailure(writeRequests.GetError());
      93        3955 :     if (aPath.mDataVersion.HasValue())
      94             :     {
      95          12 :         attributeDataIB.DataVersion(aPath.mDataVersion.Value());
      96          12 :         mHasDataVersion = true;
      97             :     }
      98        3955 :     ReturnErrorOnFailure(attributeDataIB.GetError());
      99        3955 :     AttributePathIB::Builder & path = attributeDataIB.CreatePath();
     100             : 
     101             :     // We are using kInvalidEndpointId just for group write requests. This is not the correct use of ConcreteDataAttributePath.
     102             :     // TODO: update AttributePathParams or ConcreteDataAttributePath for a class supports both nullable list index and missing
     103             :     // endpoint id.
     104        3955 :     if (aPath.mEndpointId != kInvalidEndpointId)
     105             :     {
     106        3955 :         path.Endpoint(aPath.mEndpointId);
     107             :     }
     108        3955 :     path.Cluster(aPath.mClusterId).Attribute(aPath.mAttributeId);
     109        3955 :     if (aPath.IsListItemOperation())
     110             :     {
     111        3473 :         if (aPath.mListOp == ConcreteDataAttributePath::ListOperation::AppendItem)
     112             :         {
     113        3473 :             path.ListIndex(DataModel::NullNullable);
     114             :         }
     115             :         else
     116             :         {
     117             :             // We do not support other list operations (i.e. update, delete etc) for now.
     118           0 :             return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
     119             :         }
     120             :     }
     121        3955 :     ReturnErrorOnFailure(path.EndOfAttributePathIB());
     122             : 
     123        3769 :     return CHIP_NO_ERROR;
     124             : }
     125             : 
     126        2554 : CHIP_ERROR WriteClient::FinishAttributeIB()
     127             : {
     128        2554 :     AttributeDataIB::Builder & attributeDataIB = mWriteRequestBuilder.GetWriteRequests().GetAttributeDataIBBuilder();
     129        2554 :     ReturnErrorOnFailure(attributeDataIB.EndOfAttributeDataIB());
     130        2547 :     MoveToState(State::AddAttribute);
     131        2547 :     return CHIP_NO_ERROR;
     132             : }
     133             : 
     134        3769 : TLV::TLVWriter * WriteClient::GetAttributeDataIBTLVWriter()
     135             : {
     136        3769 :     return mWriteRequestBuilder.GetWriteRequests().GetAttributeDataIBBuilder().GetWriter();
     137             : }
     138             : 
     139        1788 : CHIP_ERROR WriteClient::FinalizeMessage(bool aHasMoreChunks)
     140             : {
     141        1788 :     System::PacketBufferHandle packet;
     142        1788 :     VerifyOrReturnError(mState == State::AddAttribute, CHIP_ERROR_INCORRECT_STATE);
     143             : 
     144        1788 :     TLV::TLVWriter * writer = mWriteRequestBuilder.GetWriter();
     145        1788 :     ReturnErrorCodeIf(writer == nullptr, CHIP_ERROR_INCORRECT_STATE);
     146        1788 :     ReturnErrorOnFailure(writer->UnreserveBuffer(kReservedSizeForTLVEncodingOverhead));
     147             : 
     148        1788 :     ReturnErrorOnFailure(mWriteRequestBuilder.GetWriteRequests().EndOfAttributeDataIBs());
     149             : 
     150        1788 :     ReturnErrorOnFailure(mWriteRequestBuilder.MoreChunkedMessages(aHasMoreChunks).EndOfWriteRequestMessage());
     151        1788 :     ReturnErrorOnFailure(mMessageWriter.Finalize(&packet));
     152        1788 :     mChunks.AddToEnd(std::move(packet));
     153        1788 :     return CHIP_NO_ERROR;
     154        1788 : }
     155             : 
     156         483 : CHIP_ERROR WriteClient::EnsureMessage()
     157             : {
     158         483 :     if (mState != State::AddAttribute)
     159             :     {
     160         477 :         return StartNewMessage();
     161             :     }
     162           6 :     return CHIP_NO_ERROR;
     163             : }
     164             : 
     165        1841 : CHIP_ERROR WriteClient::StartNewMessage()
     166             : {
     167        1841 :     uint16_t reservedSize = 0;
     168             : 
     169        1841 :     if (mState == State::AddAttribute)
     170             :     {
     171        1364 :         ReturnErrorOnFailure(FinalizeMessage(true));
     172             :     }
     173             : 
     174             :     // Do not allow timed request with chunks.
     175        1841 :     VerifyOrReturnError(!(mTimedWriteTimeoutMs.HasValue() && !mChunks.IsNull()), CHIP_ERROR_NO_MEMORY);
     176             : 
     177        1841 :     System::PacketBufferHandle packet = System::PacketBufferHandle::New(kMaxSecureSduLengthBytes);
     178        1841 :     VerifyOrReturnError(!packet.IsNull(), CHIP_ERROR_NO_MEMORY);
     179             : 
     180             :     // Always limit the size of the packet to fit within kMaxSecureSduLengthBytes regardless of the available buffer capacity.
     181        1841 :     if (packet->AvailableDataLength() > kMaxSecureSduLengthBytes)
     182             :     {
     183           0 :         reservedSize = static_cast<uint16_t>(packet->AvailableDataLength() - kMaxSecureSduLengthBytes);
     184             :     }
     185             : 
     186             :     // ... and we need to reserve some extra space for the MIC field.
     187        1841 :     reservedSize = static_cast<uint16_t>(reservedSize + Crypto::CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES);
     188             : 
     189             :     // ... and the overhead for end of AttributeDataIBs (end of container), more chunks flag, end of WriteRequestMessage (another
     190             :     // end of container).
     191        1841 :     reservedSize = static_cast<uint16_t>(reservedSize + kReservedSizeForTLVEncodingOverhead);
     192             : 
     193             : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
     194             :     // ... and for unit tests.
     195        1841 :     reservedSize = static_cast<uint16_t>(reservedSize + mReservedSize);
     196             : #endif
     197             : 
     198        1841 :     mMessageWriter.Init(std::move(packet));
     199             : 
     200        1841 :     ReturnErrorOnFailure(mMessageWriter.ReserveBuffer(reservedSize));
     201             : 
     202        1841 :     ReturnErrorOnFailure(mWriteRequestBuilder.Init(&mMessageWriter));
     203        1841 :     mWriteRequestBuilder.SuppressResponse(mSuppressResponse);
     204        1841 :     mWriteRequestBuilder.TimedRequest(mTimedWriteTimeoutMs.HasValue());
     205        1841 :     ReturnErrorOnFailure(mWriteRequestBuilder.GetError());
     206        1841 :     mWriteRequestBuilder.CreateWriteRequests();
     207        1841 :     ReturnErrorOnFailure(mWriteRequestBuilder.GetError());
     208             : 
     209        1841 :     TLV::TLVWriter * writer = mWriteRequestBuilder.GetWriter();
     210        1841 :     VerifyOrReturnError(writer != nullptr, CHIP_ERROR_INCORRECT_STATE);
     211             : 
     212        1841 :     return CHIP_NO_ERROR;
     213        1841 : }
     214             : 
     215           0 : CHIP_ERROR WriteClient::TryPutSinglePreencodedAttributeWritePayload(const ConcreteDataAttributePath & attributePath,
     216             :                                                                     const TLV::TLVReader & data)
     217             : {
     218             :     TLV::TLVReader dataToWrite;
     219           0 :     dataToWrite.Init(data);
     220             : 
     221           0 :     TLV::TLVWriter * writer = nullptr;
     222             : 
     223           0 :     ReturnErrorOnFailure(PrepareAttributeIB(attributePath));
     224           0 :     VerifyOrReturnError((writer = GetAttributeDataIBTLVWriter()) != nullptr, CHIP_ERROR_INCORRECT_STATE);
     225           0 :     ReturnErrorOnFailure(writer->CopyElement(TLV::ContextTag(AttributeDataIB::Tag::kData), dataToWrite));
     226           0 :     ReturnErrorOnFailure(FinishAttributeIB());
     227           0 :     return CHIP_NO_ERROR;
     228             : }
     229             : 
     230           0 : CHIP_ERROR WriteClient::PutSinglePreencodedAttributeWritePayload(const chip::app::ConcreteDataAttributePath & attributePath,
     231             :                                                                  const TLV::TLVReader & data)
     232             : {
     233           0 :     TLV::TLVWriter backupWriter;
     234             : 
     235           0 :     mWriteRequestBuilder.GetWriteRequests().Checkpoint(backupWriter);
     236             : 
     237             :     // First attempt to write this attribute.
     238           0 :     CHIP_ERROR err = TryPutSinglePreencodedAttributeWritePayload(attributePath, data);
     239           0 :     if (err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL)
     240             :     {
     241             :         // If it failed with no memory, then we create a new chunk for it.
     242           0 :         mWriteRequestBuilder.GetWriteRequests().Rollback(backupWriter);
     243           0 :         ReturnErrorOnFailure(StartNewMessage());
     244           0 :         err = TryPutSinglePreencodedAttributeWritePayload(attributePath, data);
     245             :         // Since we have created a new chunk for this element, the encode is expected to succeed.
     246             :     }
     247           0 :     return err;
     248             : }
     249             : 
     250           0 : CHIP_ERROR WriteClient::PutPreencodedAttribute(const ConcreteDataAttributePath & attributePath, const TLV::TLVReader & data)
     251             : {
     252           0 :     ReturnErrorOnFailure(EnsureMessage());
     253             : 
     254             :     // ListIndex is missing and the data is an array -- we are writing a whole list.
     255           0 :     if (!attributePath.IsListOperation() && data.GetType() == TLV::TLVType::kTLVType_Array)
     256             :     {
     257             :         TLV::TLVReader dataReader;
     258             :         TLV::TLVReader valueReader;
     259           0 :         CHIP_ERROR err = CHIP_NO_ERROR;
     260             : 
     261           0 :         ConcreteDataAttributePath path = attributePath;
     262             : 
     263           0 :         dataReader.Init(data);
     264           0 :         dataReader.OpenContainer(valueReader);
     265             : 
     266             :         // Encode an empty list for the chunking protocol.
     267           0 :         ReturnErrorOnFailure(EncodeSingleAttributeDataIB(path, DataModel::List<uint8_t>()));
     268             : 
     269           0 :         if (err == CHIP_NO_ERROR)
     270             :         {
     271           0 :             path.mListOp = ConcreteDataAttributePath::ListOperation::AppendItem;
     272           0 :             while ((err = valueReader.Next()) == CHIP_NO_ERROR)
     273             :             {
     274           0 :                 ReturnErrorOnFailure(PutSinglePreencodedAttributeWritePayload(path, valueReader));
     275             :             }
     276             :         }
     277             : 
     278           0 :         if (err == CHIP_END_OF_TLV)
     279             :         {
     280           0 :             err = CHIP_NO_ERROR;
     281             :         }
     282           0 :         return err;
     283           0 :     }
     284             :     // We are writing a non-list attribute, or we are writing a single element of a list.
     285           0 :     return PutSinglePreencodedAttributeWritePayload(attributePath, data);
     286             : }
     287             : 
     288        5128 : const char * WriteClient::GetStateStr() const
     289             : {
     290             : #if CHIP_DETAIL_LOGGING
     291        5128 :     switch (mState)
     292             :     {
     293           0 :     case State::Initialized:
     294           0 :         return "Initialized";
     295             : 
     296        2547 :     case State::AddAttribute:
     297        2547 :         return "AddAttribute";
     298             : 
     299           0 :     case State::AwaitingTimedStatus:
     300           0 :         return "AwaitingTimedStatus";
     301             : 
     302        1734 :     case State::AwaitingResponse:
     303        1734 :         return "AwaitingResponse";
     304             : 
     305         420 :     case State::ResponseReceived:
     306         420 :         return "ResponseReceived";
     307             : 
     308         427 :     case State::AwaitingDestruction:
     309         427 :         return "AwaitingDestruction";
     310             :     }
     311             : #endif // CHIP_DETAIL_LOGGING
     312           0 :     return "N/A";
     313             : }
     314             : 
     315        5128 : void WriteClient::MoveToState(const State aTargetState)
     316             : {
     317        5128 :     mState = aTargetState;
     318        5128 :     ChipLogDetail(DataManagement, "WriteClient moving to [%10.10s]", GetStateStr());
     319        5128 : }
     320             : 
     321         424 : CHIP_ERROR WriteClient::SendWriteRequest(const SessionHandle & session, System::Clock::Timeout timeout)
     322             : {
     323         424 :     CHIP_ERROR err = CHIP_NO_ERROR;
     324             : 
     325         424 :     VerifyOrExit(mState == State::AddAttribute, err = CHIP_ERROR_INCORRECT_STATE);
     326             : 
     327         424 :     err = FinalizeMessage(false /* hasMoreChunks */);
     328         424 :     SuccessOrExit(err);
     329             : 
     330             :     {
     331             :         // Create a new exchange context.
     332         424 :         auto exchange = mpExchangeMgr->NewContext(session, this);
     333         424 :         VerifyOrExit(exchange != nullptr, err = CHIP_ERROR_NO_MEMORY);
     334             : 
     335         424 :         mExchangeCtx.Grab(exchange);
     336             :     }
     337             : 
     338         424 :     VerifyOrReturnError(!(mExchangeCtx->IsGroupExchangeContext() && mHasDataVersion), CHIP_ERROR_INVALID_MESSAGE_TYPE);
     339             : 
     340         424 :     if (timeout == System::Clock::kZero)
     341             :     {
     342         424 :         mExchangeCtx->UseSuggestedResponseTimeout(app::kExpectedIMProcessingTime);
     343             :     }
     344             :     else
     345             :     {
     346           0 :         mExchangeCtx->SetResponseTimeout(timeout);
     347             :     }
     348             : 
     349         424 :     if (mTimedWriteTimeoutMs.HasValue())
     350             :     {
     351           0 :         err = TimedRequest::Send(mExchangeCtx.Get(), mTimedWriteTimeoutMs.Value());
     352           0 :         SuccessOrExit(err);
     353           0 :         MoveToState(State::AwaitingTimedStatus);
     354             :     }
     355             :     else
     356             :     {
     357         424 :         err = SendWriteRequest();
     358         424 :         SuccessOrExit(err);
     359             :     }
     360             : 
     361         424 : exit:
     362         424 :     if (err != CHIP_NO_ERROR)
     363             :     {
     364           0 :         ChipLogError(DataManagement, "Write client failed to SendWriteRequest: %" CHIP_ERROR_FORMAT, err.Format());
     365             :     }
     366             :     else
     367             :     {
     368             :         // TODO: Ideally this would happen async, but to make sure that we
     369             :         // handle this object dying (e.g. due to IM enging shutdown) while the
     370             :         // async bits are pending we'd need to malloc some state bit that we can
     371             :         // twiddle if we die.  For now just do the OnDone callback sync.
     372         424 :         if (session->IsGroupSession())
     373             :         {
     374             :             // Always shutdown on Group communication
     375           1 :             ChipLogDetail(DataManagement, "Closing on group Communication ");
     376             : 
     377             :             // Tell the application to release the object.
     378             :             // TODO: Consumers expect to hand off ownership of the WriteClient and wait for OnDone
     379             :             // after SendWriteRequest returns success.  Calling OnDone before returning is weird.
     380             :             // Need to refactor the code to avoid this.
     381           1 :             Close();
     382             :         }
     383             :     }
     384             : 
     385         424 :     return err;
     386             : }
     387             : 
     388        1734 : CHIP_ERROR WriteClient::SendWriteRequest()
     389             : {
     390             :     using namespace Protocols::InteractionModel;
     391             :     using namespace Messaging;
     392             : 
     393        1734 :     System::PacketBufferHandle data = mChunks.PopHead();
     394             : 
     395        1734 :     bool isGroupWrite = mExchangeCtx->IsGroupExchangeContext();
     396        1734 :     if (!mChunks.IsNull() && isGroupWrite)
     397             :     {
     398             :         // Reject this request if we have more than one chunk (mChunks is not null after PopHead()), and this is a group
     399             :         // exchange context.
     400           0 :         return CHIP_ERROR_INCORRECT_STATE;
     401             :     }
     402             : 
     403             :     // kExpectResponse is ignored by ExchangeContext in case of groupcast
     404        1734 :     ReturnErrorOnFailure(mExchangeCtx->SendMessage(MsgType::WriteRequest, std::move(data), SendMessageFlags::kExpectResponse));
     405             : 
     406        1734 :     MoveToState(State::AwaitingResponse);
     407        1734 :     return CHIP_NO_ERROR;
     408        1734 : }
     409             : 
     410        1730 : CHIP_ERROR WriteClient::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
     411             :                                           System::PacketBufferHandle && aPayload)
     412             : {
     413             :     using namespace Protocols::InteractionModel;
     414             : 
     415        3460 :     if (mState == State::AwaitingResponse &&
     416             :         // We had sent the last chunk of data, and received all responses
     417        1730 :         mChunks.IsNull())
     418             :     {
     419         420 :         MoveToState(State::ResponseReceived);
     420             :     }
     421             : 
     422        1730 :     CHIP_ERROR err          = CHIP_NO_ERROR;
     423        1730 :     bool sendStatusResponse = false;
     424             :     // Assert that the exchange context matches the client's current context.
     425             :     // This should never fail because even if SendWriteRequest is called
     426             :     // back-to-back, the second call will call Close() on the first exchange,
     427             :     // which clears the OnMessageReceived callback.
     428        1730 :     VerifyOrExit(apExchangeContext == mExchangeCtx.Get(), err = CHIP_ERROR_INCORRECT_STATE);
     429             : 
     430        1730 :     sendStatusResponse = true;
     431             : 
     432        1730 :     if (mState == State::AwaitingTimedStatus)
     433             :     {
     434           0 :         if (aPayloadHeader.HasMessageType(MsgType::StatusResponse))
     435             :         {
     436           0 :             CHIP_ERROR statusError = CHIP_NO_ERROR;
     437           0 :             SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError));
     438           0 :             sendStatusResponse = false;
     439           0 :             SuccessOrExit(err = statusError);
     440           0 :             err = SendWriteRequest();
     441             :         }
     442             :         else
     443             :         {
     444           0 :             err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
     445             :         }
     446             :         // Skip all other processing here (which is for the response to the
     447             :         // write request), no matter whether err is success or not.
     448           0 :         goto exit;
     449             :     }
     450             : 
     451        1730 :     if (aPayloadHeader.HasMessageType(MsgType::WriteResponse))
     452             :     {
     453        1726 :         err = ProcessWriteResponseMessage(std::move(aPayload));
     454        1726 :         SuccessOrExit(err);
     455        1725 :         sendStatusResponse = false;
     456        1725 :         if (!mChunks.IsNull())
     457             :         {
     458             :             // Send the next chunk.
     459        1310 :             SuccessOrExit(err = SendWriteRequest());
     460             :         }
     461             :     }
     462           4 :     else if (aPayloadHeader.HasMessageType(MsgType::StatusResponse))
     463             :     {
     464           3 :         CHIP_ERROR statusError = CHIP_NO_ERROR;
     465           5 :         SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError));
     466           2 :         SuccessOrExit(err = statusError);
     467           0 :         err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
     468             :     }
     469             :     else
     470             :     {
     471           1 :         err = CHIP_ERROR_INVALID_MESSAGE_TYPE;
     472             :     }
     473             : 
     474        1730 : exit:
     475        1730 :     if (mpCallback != nullptr)
     476             :     {
     477        1730 :         if (err != CHIP_NO_ERROR)
     478             :         {
     479           5 :             mpCallback->OnError(this, err);
     480             :         }
     481             :     }
     482             : 
     483        1730 :     if (sendStatusResponse)
     484             :     {
     485           5 :         StatusResponse::Send(Status::InvalidAction, apExchangeContext, false /*aExpectResponse*/);
     486             :     }
     487             : 
     488        1730 :     if (mState != State::AwaitingResponse)
     489             :     {
     490         420 :         Close();
     491             :     }
     492             :     // Else we got a response to a Timed Request and just sent the write.
     493             : 
     494        1730 :     return err;
     495             : }
     496             : 
     497           5 : void WriteClient::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext)
     498             : {
     499           5 :     ChipLogError(DataManagement, "Time out! failed to receive write response from Exchange: " ChipLogFormatExchange,
     500             :                  ChipLogValueExchange(apExchangeContext));
     501             : 
     502           5 :     if (mpCallback != nullptr)
     503             :     {
     504           5 :         mpCallback->OnError(this, CHIP_ERROR_TIMEOUT);
     505             :     }
     506           5 :     Close();
     507           5 : }
     508             : 
     509        2481 : CHIP_ERROR WriteClient::ProcessAttributeStatusIB(AttributeStatusIB::Parser & aAttributeStatusIB)
     510             : {
     511        2481 :     CHIP_ERROR err = CHIP_NO_ERROR;
     512        2481 :     AttributePathIB::Parser attributePathParser;
     513        2481 :     StatusIB statusIB;
     514        2481 :     StatusIB::Parser StatusIBParser;
     515        2481 :     ConcreteDataAttributePath attributePath;
     516             : 
     517        2481 :     err = aAttributeStatusIB.GetPath(&attributePathParser);
     518        2481 :     SuccessOrExit(err);
     519             : 
     520        2481 :     err = attributePathParser.GetConcreteAttributePath(attributePath);
     521        2481 :     SuccessOrExit(err);
     522             : 
     523        2481 :     err = aAttributeStatusIB.GetErrorStatus(&(StatusIBParser));
     524        2481 :     if (CHIP_NO_ERROR == err)
     525             :     {
     526        2481 :         err = StatusIBParser.DecodeStatusIB(statusIB);
     527        2481 :         SuccessOrExit(err);
     528        2481 :         if (mpCallback != nullptr)
     529             :         {
     530        2481 :             mpCallback->OnResponse(this, attributePath, statusIB);
     531             :         }
     532             :     }
     533             : 
     534           0 : exit:
     535        2481 :     return err;
     536        2481 : }
     537             : 
     538             : } // namespace app
     539             : } // namespace chip

Generated by: LCOV version 1.14