diff --git a/src/app/CommandHandler.cpp b/src/app/CommandHandler.cpp index d17c4f36cd33e1..57c3bbd6d39951 100644 --- a/src/app/CommandHandler.cpp +++ b/src/app/CommandHandler.cpp @@ -39,6 +39,7 @@ namespace chip { namespace app { +using Status = Protocols::InteractionModel::Status; CommandHandler::CommandHandler(Callback * apCallback) : mExchangeCtx(*this), mpCallback(apCallback), mSuppressResponse(false) {} @@ -65,11 +66,13 @@ CHIP_ERROR CommandHandler::AllocateBuffer() return CHIP_NO_ERROR; } -CHIP_ERROR CommandHandler::OnInvokeCommandRequest(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, - System::PacketBufferHandle && payload, bool isTimedInvoke) +Status CommandHandler::OnInvokeCommandRequest(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, + System::PacketBufferHandle && payload, bool isTimedInvoke) { System::PacketBufferHandle response; - VerifyOrReturnError(mState == State::Idle, CHIP_ERROR_INCORRECT_STATE); + + VerifyOrReturnError(ec != nullptr, Status::Failure); + VerifyOrReturnError(mState == State::Idle, Status::Failure); // NOTE: we already know this is an InvokeCommand Request message because we explicitly registered with the // Exchange Manager for unsolicited InvokeCommand Requests. @@ -81,13 +84,12 @@ CHIP_ERROR CommandHandler::OnInvokeCommandRequest(Messaging::ExchangeContext * e // response too. Figure out at what point it's our responsibility to // handler errors vs our caller's. Handle workHandle(this); - mExchangeCtx->WillSendMessage(); - ReturnErrorOnFailure(ProcessInvokeRequest(std::move(payload), isTimedInvoke)); - return CHIP_NO_ERROR; + mExchangeCtx->WillSendMessage(); + return ProcessInvokeRequest(std::move(payload), isTimedInvoke); } -CHIP_ERROR CommandHandler::ProcessInvokeRequest(System::PacketBufferHandle && payload, bool isTimedInvoke) +Status CommandHandler::ProcessInvokeRequest(System::PacketBufferHandle && payload, bool isTimedInvoke) { CHIP_ERROR err = CHIP_NO_ERROR; System::PacketBufferTLVReader reader; @@ -95,30 +97,15 @@ CHIP_ERROR CommandHandler::ProcessInvokeRequest(System::PacketBufferHandle && pa InvokeRequestMessage::Parser invokeRequestMessage; InvokeRequests::Parser invokeRequests; reader.Init(std::move(payload)); - ReturnErrorOnFailure(invokeRequestMessage.Init(reader)); + VerifyOrReturnError(invokeRequestMessage.Init(reader) == CHIP_NO_ERROR, Status::InvalidAction); #if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK - ReturnErrorOnFailure(invokeRequestMessage.CheckSchemaValidity()); + VerifyOrReturnError(invokeRequestMessage.CheckSchemaValidity() == CHIP_NO_ERROR, Status::InvalidAction); #endif - ReturnErrorOnFailure(invokeRequestMessage.GetSuppressResponse(&mSuppressResponse)); - ReturnErrorOnFailure(invokeRequestMessage.GetTimedRequest(&mTimedRequest)); - ReturnErrorOnFailure(invokeRequestMessage.GetInvokeRequests(&invokeRequests)); - - VerifyOrReturnError(mExchangeCtx, CHIP_ERROR_INCORRECT_STATE); - - if (mTimedRequest != isTimedInvoke) - { - // The message thinks it should be part of a timed interaction but it's - // not, or vice versa. Spec says to Respond with UNSUPPORTED_ACCESS. - err = StatusResponse::Send(Protocols::InteractionModel::Status::UnsupportedAccess, mExchangeCtx.Get(), - /* aExpectResponse = */ false); - if (err == CHIP_NO_ERROR) - { - mSentStatusResponse = true; - } - - return err; - } + VerifyOrReturnError(invokeRequestMessage.GetSuppressResponse(&mSuppressResponse) == CHIP_NO_ERROR, Status::InvalidAction); + VerifyOrReturnError(invokeRequestMessage.GetTimedRequest(&mTimedRequest) == CHIP_NO_ERROR, Status::InvalidAction); + VerifyOrReturnError(invokeRequestMessage.GetInvokeRequests(&invokeRequests) == CHIP_NO_ERROR, Status::InvalidAction); + VerifyOrReturnError(mTimedRequest == isTimedInvoke, Status::UnsupportedAccess); invokeRequests.GetReader(&invokeRequestsReader); { @@ -126,22 +113,22 @@ CHIP_ERROR CommandHandler::ProcessInvokeRequest(System::PacketBufferHandle && pa // IM Engine will send a status response. size_t commandCount = 0; TLV::Utilities::Count(invokeRequestsReader, commandCount, false /* recurse */); - VerifyOrReturnError(commandCount == 1, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + VerifyOrReturnError(commandCount == 1, Status::Failure); } while (CHIP_NO_ERROR == (err = invokeRequestsReader.Next())) { - VerifyOrReturnError(TLV::AnonymousTag() == invokeRequestsReader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG); + VerifyOrReturnError(TLV::AnonymousTag() == invokeRequestsReader.GetTag(), Status::InvalidAction); CommandDataIB::Parser commandData; - ReturnErrorOnFailure(commandData.Init(invokeRequestsReader)); + VerifyOrReturnError(commandData.Init(invokeRequestsReader) == CHIP_NO_ERROR, Status::InvalidAction); if (mExchangeCtx->IsGroupExchangeContext()) { - ReturnErrorOnFailure(ProcessGroupCommandDataIB(commandData)); + VerifyOrReturnError(ProcessGroupCommandDataIB(commandData) == CHIP_NO_ERROR, Status::Failure); } else { - ReturnErrorOnFailure(ProcessCommandDataIB(commandData)); + VerifyOrReturnError(ProcessCommandDataIB(commandData) == CHIP_NO_ERROR, Status::Failure); } } @@ -150,8 +137,16 @@ CHIP_ERROR CommandHandler::ProcessInvokeRequest(System::PacketBufferHandle && pa { err = CHIP_NO_ERROR; } - ReturnErrorOnFailure(err); - return invokeRequestMessage.ExitContainer(); + VerifyOrReturnError(err == CHIP_NO_ERROR, Status::InvalidAction); + VerifyOrReturnError(invokeRequestMessage.ExitContainer() == CHIP_NO_ERROR, Status::InvalidAction); + return Status::Success; +} + +CHIP_ERROR CommandHandler::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, + System::PacketBufferHandle && aPayload) +{ + ChipLogDetail(DataManagement, "CommandHandler: Unexpected message type %d", aPayloadHeader.GetMessageType()); + return StatusResponse::Send(Status::InvalidAction, mExchangeCtx.Get(), false /*aExpectResponse*/); } void CommandHandler::Close() @@ -186,7 +181,7 @@ void CommandHandler::DecrementHoldOff() return; } - if (!mExchangeCtx->IsGroupExchangeContext() && !mSentStatusResponse) + if (!mExchangeCtx->IsGroupExchangeContext()) { CHIP_ERROR err = SendCommandResponse(); if (err != CHIP_NO_ERROR) @@ -250,7 +245,6 @@ CHIP_ERROR CommandHandler::ProcessCommandDataIB(CommandDataIB::Parser & aCommand err = commandPath.GetEndpointId(&concretePath.mEndpointId); SuccessOrExit(err); - using Protocols::InteractionModel::Status; { Status commandExists = mpCallback->CommandExists(concretePath); if (commandExists != Status::Success) @@ -284,7 +278,7 @@ CHIP_ERROR CommandHandler::ProcessCommandDataIB(CommandDataIB::Parser & aCommand { // TODO: when wildcard invokes are supported, discard a // wildcard-expanded path instead of returning a status. - return AddStatus(concretePath, Protocols::InteractionModel::Status::NeedsTimedInteraction); + return AddStatus(concretePath, Status::NeedsTimedInteraction); } if (CommandIsFabricScoped(concretePath.mClusterId, concretePath.mCommandId)) @@ -399,7 +393,7 @@ CHIP_ERROR CommandHandler::ProcessGroupCommandDataIB(CommandDataIB::Parser & aCo const ConcreteCommandPath concretePath(mapping.endpoint_id, clusterId, commandId); - if (mpCallback->CommandExists(concretePath) != Protocols::InteractionModel::Status::Success) + if (mpCallback->CommandExists(concretePath) != Status::Success) { ChipLogDetail(DataManagement, "No command " ChipLogFormatMEI " in Cluster " ChipLogFormatMEI " on Endpoint 0x%x", ChipLogValueMEI(commandId), ChipLogValueMEI(clusterId), mapping.endpoint_id); @@ -452,20 +446,18 @@ CHIP_ERROR CommandHandler::AddStatusInternal(const ConcreteCommandPath & aComman return FinishStatus(); } -CHIP_ERROR CommandHandler::AddStatus(const ConcreteCommandPath & aCommandPath, const Protocols::InteractionModel::Status aStatus) +CHIP_ERROR CommandHandler::AddStatus(const ConcreteCommandPath & aCommandPath, const Status aStatus) { return AddStatusInternal(aCommandPath, StatusIB(aStatus)); } CHIP_ERROR CommandHandler::AddClusterSpecificSuccess(const ConcreteCommandPath & aCommandPath, ClusterStatus aClusterStatus) { - using Protocols::InteractionModel::Status; return AddStatusInternal(aCommandPath, StatusIB(Status::Success, aClusterStatus)); } CHIP_ERROR CommandHandler::AddClusterSpecificFailure(const ConcreteCommandPath & aCommandPath, ClusterStatus aClusterStatus) { - using Protocols::InteractionModel::Status; return AddStatusInternal(aCommandPath, StatusIB(Status::Failure, aClusterStatus)); } @@ -635,7 +627,7 @@ const char * CommandHandler::GetStateStr() const void CommandHandler::MoveToState(const State aTargetState) { mState = aTargetState; - ChipLogDetail(DataManagement, "ICR moving to [%10.10s]", GetStateStr()); + ChipLogDetail(DataManagement, "Command handler moving to [%10.10s]", GetStateStr()); } } // namespace app diff --git a/src/app/CommandHandler.h b/src/app/CommandHandler.h index da198cad0af6ef..50922bdb8b58ef 100644 --- a/src/app/CommandHandler.h +++ b/src/app/CommandHandler.h @@ -139,15 +139,15 @@ class CommandHandler : public Messaging::ExchangeDelegate * transaction (i.e. was preceded by a Timed Request). If we reach here, * the timer verification has already been done. */ - CHIP_ERROR OnInvokeCommandRequest(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, - System::PacketBufferHandle && payload, bool isTimedInvoke); + Protocols::InteractionModel::Status OnInvokeCommandRequest(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, + System::PacketBufferHandle && payload, bool isTimedInvoke); CHIP_ERROR AddStatus(const ConcreteCommandPath & aCommandPath, const Protocols::InteractionModel::Status aStatus); CHIP_ERROR AddClusterSpecificSuccess(const ConcreteCommandPath & aCommandPath, ClusterStatus aClusterStatus); CHIP_ERROR AddClusterSpecificFailure(const ConcreteCommandPath & aCommandPath, ClusterStatus aClusterStatus); - CHIP_ERROR ProcessInvokeRequest(System::PacketBufferHandle && payload, bool isTimedInvoke); + Protocols::InteractionModel::Status ProcessInvokeRequest(System::PacketBufferHandle && payload, bool isTimedInvoke); CHIP_ERROR PrepareCommand(const ConcreteCommandPath & aCommandPath, bool aStartDataStruct = true); CHIP_ERROR FinishCommand(bool aEndDataStruct = true); CHIP_ERROR PrepareStatus(const ConcreteCommandPath & aCommandPath); @@ -250,13 +250,7 @@ class CommandHandler : public Messaging::ExchangeDelegate friend class CommandHandler::Handle; CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, - System::PacketBufferHandle && payload) override - { - // - // We shouldn't be receiving any further messages on this exchange. - // - return CHIP_ERROR_INCORRECT_STATE; - } + System::PacketBufferHandle && payload) override; void OnResponseTimeout(Messaging::ExchangeContext * ec) override { @@ -359,14 +353,13 @@ class CommandHandler : public Messaging::ExchangeDelegate Messaging::ExchangeHolder mExchangeCtx; Callback * mpCallback = nullptr; + InvokeResponseMessage::Builder mInvokeResponseBuilder; TLV::TLVType mDataElementContainerType = TLV::kTLVType_NotSpecified; size_t mPendingWork = 0; bool mSuppressResponse = false; bool mTimedRequest = false; - bool mSentStatusResponse = false; - State mState = State::Idle; chip::System::PacketBufferTLVWriter mCommandMessageWriter; TLV::TLVWriter mBackupWriter; diff --git a/src/app/CommandSender.cpp b/src/app/CommandSender.cpp index 803204dadaebcb..476a88a7dbdc79 100644 --- a/src/app/CommandSender.cpp +++ b/src/app/CommandSender.cpp @@ -120,6 +120,7 @@ CHIP_ERROR CommandSender::SendInvokeRequest() CHIP_ERROR CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) { + bool suppressErrorStatusResponse = false; if (mState == State::CommandSent) { MoveToState(State::ResponseReceived); @@ -130,7 +131,18 @@ CHIP_ERROR CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExcha if (mState == State::AwaitingTimedStatus) { - err = HandleTimedStatus(aPayloadHeader, std::move(aPayload)); + if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse)) + { + CHIP_ERROR statusError = CHIP_NO_ERROR; + SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError)); + suppressErrorStatusResponse = true; + SuccessOrExit(err = statusError); + err = SendInvokeRequest(); + } + else + { + err = CHIP_ERROR_INVALID_MESSAGE_TYPE; + } // Skip all other processing here (which is for the response to the // invoke request), no matter whether err is success or not. goto exit; @@ -143,8 +155,10 @@ CHIP_ERROR CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExcha } else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse)) { - err = StatusResponse::ProcessStatusResponse(std::move(aPayload)); - SuccessOrExit(err); + CHIP_ERROR statusError = CHIP_NO_ERROR; + SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError)); + SuccessOrExit(err = statusError); + err = CHIP_ERROR_INVALID_MESSAGE_TYPE; } else { @@ -152,11 +166,22 @@ CHIP_ERROR CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExcha } exit: - if (mpCallback != nullptr) + ResponseMessageHandled(err, apExchangeContext, suppressErrorStatusResponse); + return err; +} + +void CommandSender::ResponseMessageHandled(CHIP_ERROR aError, Messaging::ExchangeContext * apExchangeContext, + bool aSuppressErrorStatusResponse) +{ + if (aError != CHIP_NO_ERROR) { - if (err != CHIP_NO_ERROR) + if (!aSuppressErrorStatusResponse) { - mpCallback->OnError(this, err); + StatusResponse::Send(Protocols::InteractionModel::Status::InvalidAction, apExchangeContext, false /*aExpectResponse*/); + if (mpCallback != nullptr) + { + mpCallback->OnError(this, aError); + } } } @@ -164,9 +189,6 @@ CHIP_ERROR CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExcha { Close(); } - // Else we got a response to a Timed Request and just sent the invoke. - - return err; } CHIP_ERROR CommandSender::ProcessInvokeResponse(System::PacketBufferHandle && payload) @@ -369,13 +391,6 @@ TLV::TLVWriter * CommandSender::GetCommandDataIBTLVWriter() return mInvokeRequestBuilder.GetInvokeRequests().GetCommandData().GetWriter(); } -CHIP_ERROR CommandSender::HandleTimedStatus(const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) -{ - ReturnErrorOnFailure(TimedRequest::HandleResponse(aPayloadHeader, std::move(aPayload))); - - return SendInvokeRequest(); -} - CHIP_ERROR CommandSender::FinishCommand(const Optional & aTimedInvokeTimeoutMs) { ReturnErrorOnFailure(FinishCommand(/* aEndDataStruct = */ false)); @@ -430,7 +445,7 @@ const char * CommandSender::GetStateStr() const void CommandSender::MoveToState(const State aTargetState) { mState = aTargetState; - ChipLogDetail(DataManagement, "ICR moving to [%10.10s]", GetStateStr()); + ChipLogDetail(DataManagement, "Command Sender moving to [%10.10s]", GetStateStr()); } } // namespace app diff --git a/src/app/CommandSender.h b/src/app/CommandSender.h index e0ad257d5722fb..af2f933409c2c2 100644 --- a/src/app/CommandSender.h +++ b/src/app/CommandSender.h @@ -259,19 +259,13 @@ class CommandSender final : public Messaging::ExchangeDelegate CHIP_ERROR ProcessInvokeResponse(System::PacketBufferHandle && payload); CHIP_ERROR ProcessInvokeResponseIB(InvokeResponseIB::Parser & aInvokeResponse); - // Handle a message received when we are expecting a status response to a - // Timed Request. The caller is assumed to have already checked that our - // exchange context member is the one the message came in on. - // - // If the server returned an error status, that will be returned as an error - // value of CHIP_ERROR. - CHIP_ERROR HandleTimedStatus(const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload); - // Send our queued-up Invoke Request message. Assumes the exchange is ready // and mPendingInvokeData is populated. CHIP_ERROR SendInvokeRequest(); CHIP_ERROR Finalize(System::PacketBufferHandle & commandPacket); + void ResponseMessageHandled(CHIP_ERROR aError, Messaging::ExchangeContext * apExchangeContext, + bool aSuppressErrorStatusResponse); Messaging::ExchangeHolder mExchangeCtx; Callback * mpCallback = nullptr; diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index e4f76d46d113b5..eee6f9b396ed59 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -34,6 +34,9 @@ extern bool emberAfContainsAttribute(chip::EndpointId endpoint, chip::ClusterId namespace chip { namespace app { + +using Protocols::InteractionModel::Status; + InteractionModelEngine sInteractionModelEngine; InteractionModelEngine::InteractionModelEngine() {} @@ -275,28 +278,23 @@ void InteractionModelEngine::OnDone(ReadHandler & apReadObj) mReadHandlers.ReleaseObject(&apReadObj); } -CHIP_ERROR InteractionModelEngine::OnInvokeCommandRequest(Messaging::ExchangeContext * apExchangeContext, - const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload, bool aIsTimedInvoke, - Protocols::InteractionModel::Status & aStatus) +Status InteractionModelEngine::OnInvokeCommandRequest(Messaging::ExchangeContext * apExchangeContext, + const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload, + bool aIsTimedInvoke) { CommandHandler * commandHandler = mCommandHandlerObjs.CreateObject(this); if (commandHandler == nullptr) { ChipLogProgress(InteractionModel, "no resource for Invoke interaction"); - aStatus = Status::Busy; - return CHIP_ERROR_NO_MEMORY; + return Status::Busy; } - ReturnErrorOnFailure( - commandHandler->OnInvokeCommandRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), aIsTimedInvoke)); - aStatus = Status::Success; - return CHIP_NO_ERROR; + + return commandHandler->OnInvokeCommandRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), aIsTimedInvoke); } -Protocols::InteractionModel::Status InteractionModelEngine::OnReadInitialRequest(Messaging::ExchangeContext * apExchangeContext, - const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload, - ReadHandler::InteractionType aInteractionType) +Status InteractionModelEngine::OnReadInitialRequest(Messaging::ExchangeContext * apExchangeContext, + const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload, + ReadHandler::InteractionType aInteractionType) { ChipLogDetail(InteractionModel, "Received %s request", aInteractionType == ReadHandler::InteractionType::Subscribe ? "Subscribe" : "Read"); @@ -466,10 +464,8 @@ Protocols::InteractionModel::Status InteractionModelEngine::OnReadInitialRequest return StatusIB(err).mStatus; } -Protocols::InteractionModel::Status InteractionModelEngine::OnWriteRequest(Messaging::ExchangeContext * apExchangeContext, - const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload, - bool aIsTimedWrite) +Status InteractionModelEngine::OnWriteRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, + System::PacketBufferHandle && aPayload, bool aIsTimedWrite) { ChipLogDetail(InteractionModel, "Received Write request"); @@ -487,7 +483,7 @@ Protocols::InteractionModel::Status InteractionModelEngine::OnWriteRequest(Messa CHIP_ERROR InteractionModelEngine::OnTimedRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload, - Protocols::InteractionModel::Status & aStatus) + Status & aStatus) { TimedHandler * handler = mTimedHandlers.CreateObject(); if (handler == nullptr) @@ -504,19 +500,18 @@ CHIP_ERROR InteractionModelEngine::OnTimedRequest(Messaging::ExchangeContext * a return handler->OnMessageReceived(apExchangeContext, aPayloadHeader, std::move(aPayload)); } -CHIP_ERROR InteractionModelEngine::OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, - const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload) +Status InteractionModelEngine::OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, + const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) { System::PacketBufferTLVReader reader; reader.Init(aPayload.Retain()); ReportDataMessage::Parser report; - ReturnErrorOnFailure(report.Init(reader)); + VerifyOrReturnError(report.Init(reader) == CHIP_NO_ERROR, Status::InvalidSubscription); SubscriptionId subscriptionId = 0; - ReturnErrorOnFailure(report.GetSubscriptionId(&subscriptionId)); - ReturnErrorOnFailure(report.ExitContainer()); + VerifyOrReturnError(report.GetSubscriptionId(&subscriptionId) == CHIP_NO_ERROR, Status::InvalidSubscription); + VerifyOrReturnError(report.ExitContainer() == CHIP_NO_ERROR, Status::InvalidSubscription); for (auto * readClient = mpActiveReadClientList; readClient != nullptr; readClient = readClient->GetNextClient()) { @@ -530,10 +525,12 @@ CHIP_ERROR InteractionModelEngine::OnUnsolicitedReportData(Messaging::ExchangeCo continue; } - return readClient->OnUnsolicitedReportData(apExchangeContext, std::move(aPayload)); + // status report is sent inside readClient's OnUnsolicitedReportData; + readClient->OnUnsolicitedReportData(apExchangeContext, std::move(aPayload)); + return Status::Success; } - return CHIP_NO_ERROR; + return Status::InvalidSubscription; } CHIP_ERROR InteractionModelEngine::OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, @@ -550,7 +547,7 @@ CHIP_ERROR InteractionModelEngine::OnMessageReceived(Messaging::ExchangeContext { using namespace Protocols::InteractionModel; - Protocols::InteractionModel::Status status = Status::Failure; + Status status = Status::Failure; // Group Message can only be an InvokeCommandRequest or WriteRequest if (apExchangeContext->IsGroupExchangeContext() && @@ -563,7 +560,7 @@ CHIP_ERROR InteractionModelEngine::OnMessageReceived(Messaging::ExchangeContext if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::InvokeCommandRequest)) { - OnInvokeCommandRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), /* aIsTimedInvoke = */ false, status); + status = OnInvokeCommandRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), /* aIsTimedInvoke = */ false); } else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::ReadRequest)) { @@ -580,8 +577,7 @@ CHIP_ERROR InteractionModelEngine::OnMessageReceived(Messaging::ExchangeContext } else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::ReportData)) { - ReturnErrorOnFailure(OnUnsolicitedReportData(apExchangeContext, aPayloadHeader, std::move(aPayload))); - status = Status::Success; + status = OnUnsolicitedReportData(apExchangeContext, aPayloadHeader, std::move(aPayload)); } else if (aPayloadHeader.HasMessageType(MsgType::TimedRequest)) { @@ -590,6 +586,7 @@ CHIP_ERROR InteractionModelEngine::OnMessageReceived(Messaging::ExchangeContext else { ChipLogProgress(InteractionModel, "Msg type %d not supported", aPayloadHeader.GetMessageType()); + status = Status::InvalidAction; } if (status != Status::Success && !apExchangeContext->IsGroupExchangeContext()) @@ -867,9 +864,8 @@ bool InteractionModelEngine::TrimFabricForRead(FabricIndex aFabricIndex) return false; } -Protocols::InteractionModel::Status InteractionModelEngine::EnsureResourceForRead(FabricIndex aFabricIndex, - size_t aRequestedAttributePathCount, - size_t aRequestedEventPathCount) +Status InteractionModelEngine::EnsureResourceForRead(FabricIndex aFabricIndex, size_t aRequestedAttributePathCount, + size_t aRequestedEventPathCount) { #if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP && !CHIP_CONFIG_IM_FORCE_FABRIC_QUOTA_CHECK #if CONFIG_BUILD_FOR_HOST_UNIT_TEST @@ -1235,7 +1231,7 @@ void InteractionModelEngine::DispatchCommand(CommandHandler & apCommandObj, cons DispatchSingleClusterCommand(aCommandPath, apPayload, &apCommandObj); } -Protocols::InteractionModel::Status InteractionModelEngine::CommandExists(const ConcreteCommandPath & aCommandPath) +Status InteractionModelEngine::CommandExists(const ConcreteCommandPath & aCommandPath) { return ServerClusterCommandExists(aCommandPath); } @@ -1345,8 +1341,7 @@ void InteractionModelEngine::OnTimedInvoke(TimedHandler * apTimedHandler, Messag VerifyOrDie(aPayloadHeader.HasMessageType(MsgType::InvokeCommandRequest)); VerifyOrDie(!apExchangeContext->IsGroupExchangeContext()); - Status status = Status::Failure; - OnInvokeCommandRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), /* aIsTimedInvoke = */ true, status); + Status status = OnInvokeCommandRequest(apExchangeContext, aPayloadHeader, std::move(aPayload), /* aIsTimedInvoke = */ true); if (status != Status::Success) { StatusResponse::Send(status, apExchangeContext, /* aExpectResponse = */ false); diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index 208b88d29bfa60..ceba0c73b44bdb 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -155,7 +155,7 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, uint32_t GetNumActiveWriteHandlers() const; /** - * Returns the handler at a particular index within the active handler list. + * Returns the handler at a particular index within the active read handler list. */ ReadHandler * ActiveHandlerAt(unsigned int aIndex); @@ -358,12 +358,10 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, /** * Called when Interaction Model receives a Command Request message. Errors processing - * the Command Request are handled entirely within this function. The caller pre-sets status to failure and the callee is - * expected to set it to success if it does not want an automatic status response message to be sent. + * If this returns a failure status the caller should send a status response with that status. */ - CHIP_ERROR OnInvokeCommandRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload, bool aIsTimedInvoke, - Protocols::InteractionModel::Status & aStatus); + Status OnInvokeCommandRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, + System::PacketBufferHandle && aPayload, bool aIsTimedInvoke); CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) override; void OnResponseTimeout(Messaging::ExchangeContext * ec) override; @@ -392,17 +390,17 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, * expected to set it to success if it does not want an automatic status response message to be sent. */ CHIP_ERROR OnTimedRequest(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload, Protocols::InteractionModel::Status & aStatus); + System::PacketBufferHandle && aPayload, Status & aStatus); /**This function handles processing of un-solicited ReportData messages on the client, which can * only occur post subscription establishment */ - CHIP_ERROR OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload); + Status OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, + System::PacketBufferHandle && aPayload); void DispatchCommand(CommandHandler & apCommandObj, const ConcreteCommandPath & aCommandPath, TLV::TLVReader & apPayload) override; - Protocols::InteractionModel::Status CommandExists(const ConcreteCommandPath & aCommandPath) override; + Status CommandExists(const ConcreteCommandPath & aCommandPath) override; bool HasActiveRead(); diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp index 8d581db39bf319..ac1c05afd7da71 100644 --- a/src/app/ReadClient.cpp +++ b/src/app/ReadClient.cpp @@ -34,6 +34,8 @@ namespace chip { namespace app { +using Status = Protocols::InteractionModel::Status; + ReadClient::ReadClient(InteractionModelEngine * apImEngine, Messaging::ExchangeManager * apExchangeMgr, Callback & apCallback, InteractionType aInteractionType) : mExchange(*this), @@ -407,7 +409,6 @@ CHIP_ERROR ReadClient::OnMessageReceived(Messaging::ExchangeContext * apExchange System::PacketBufferHandle && aPayload) { CHIP_ERROR err = CHIP_NO_ERROR; - VerifyOrExit(!IsIdle(), err = CHIP_ERROR_INCORRECT_STATE); if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::ReportData)) @@ -424,8 +425,11 @@ CHIP_ERROR ReadClient::OnMessageReceived(Messaging::ExchangeContext * apExchange else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse)) { VerifyOrExit(apExchangeContext == mExchange.Get(), err = CHIP_ERROR_INCORRECT_STATE); - err = StatusResponse::ProcessStatusResponse(std::move(aPayload)); - SuccessOrExit(err); + + CHIP_ERROR statusError = CHIP_NO_ERROR; + SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError)); + SuccessOrExit(err = statusError); + err = CHIP_ERROR_INVALID_MESSAGE_TYPE; } else { @@ -433,12 +437,21 @@ CHIP_ERROR ReadClient::OnMessageReceived(Messaging::ExchangeContext * apExchange } exit: - if ((!IsSubscriptionType() && !mPendingMoreChunks) || err != CHIP_NO_ERROR) + ResponseMessageHandled(err, apExchangeContext); + return err; +} + +void ReadClient::ResponseMessageHandled(CHIP_ERROR aError, Messaging::ExchangeContext * apExchangeContext) +{ + if (aError != CHIP_NO_ERROR) { - Close(err); + StatusResponse::Send(Status::InvalidAction, apExchangeContext, false /*aExpectResponse*/); } - return err; + if ((!IsSubscriptionType() && !mPendingMoreChunks) || aError != CHIP_NO_ERROR) + { + Close(aError); + } } CHIP_ERROR ReadClient::OnUnsolicitedReportData(Messaging::ExchangeContext * apExchangeContext, @@ -571,9 +584,8 @@ CHIP_ERROR ReadClient::ProcessReportData(System::PacketBufferHandle && aPayload) if (!suppressResponse) { bool noResponseExpected = IsSubscriptionActive() && !mPendingMoreChunks; - err = StatusResponse::Send(err == CHIP_NO_ERROR ? Protocols::InteractionModel::Status::Success - : Protocols::InteractionModel::Status::InvalidSubscription, - mExchange.Get(), !noResponseExpected); + err = StatusResponse::Send(err == CHIP_NO_ERROR ? Status::Success : Status::InvalidSubscription, mExchange.Get(), + !noResponseExpected); } mIsPrimingReports = false; diff --git a/src/app/ReadClient.h b/src/app/ReadClient.h index 78291fa5fddc7a..9e306eecc382c0 100644 --- a/src/app/ReadClient.h +++ b/src/app/ReadClient.h @@ -451,7 +451,7 @@ class ReadClient : public Messaging::ExchangeDelegate CHIP_ERROR ProcessAttributePath(AttributePathIB::Parser & aAttributePath, ConcreteDataAttributePath & aClusterInfo); CHIP_ERROR ProcessReportData(System::PacketBufferHandle && aPayload); const char * GetStateStr() const; - + void ResponseMessageHandled(CHIP_ERROR aError, Messaging::ExchangeContext * apExchangeContext); /* * Checks if we should re-subscribe based on the specified re-subscription policy. If we should, re-subscription is scheduled * aNextResubscribeIntervalMsec is updated accordingly, and true is returned. diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index e40d8bf007213f..89174951ea7162 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -36,6 +36,7 @@ namespace chip { namespace app { +using Status = Protocols::InteractionModel::Status; ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, InteractionType aInteractionType) : @@ -116,9 +117,12 @@ CHIP_ERROR ReadHandler::OnInitialRequest(System::PacketBufferHandle && aPayload) CHIP_ERROR ReadHandler::OnStatusResponse(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload) { - CHIP_ERROR err = CHIP_NO_ERROR; - err = StatusResponse::ProcessStatusResponse(std::move(aPayload)); - SuccessOrExit(err); + CHIP_ERROR err = CHIP_NO_ERROR; + bool suppressErrorStatusResponse = false; + CHIP_ERROR statusError = CHIP_NO_ERROR; + SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError)); + suppressErrorStatusResponse = true; + SuccessOrExit(err = statusError); switch (mState) { case HandlerState::AwaitingReportResponse: @@ -163,6 +167,10 @@ CHIP_ERROR ReadHandler::OnStatusResponse(Messaging::ExchangeContext * apExchange exit: if (err != CHIP_NO_ERROR) { + if (!suppressErrorStatusResponse) + { + err = StatusResponse::Send(Status::InvalidAction, apExchangeContext, false /*aExpectResponse*/); + } Close(); } @@ -256,7 +264,8 @@ CHIP_ERROR ReadHandler::OnMessageReceived(Messaging::ExchangeContext * apExchang } else { - err = OnUnknownMsgType(apExchangeContext, aPayloadHeader, std::move(aPayload)); + ChipLogDetail(DataManagement, "Unexpected message type %d", aPayloadHeader.GetMessageType()); + err = OnUnknownMsgType(); } return err; } @@ -268,12 +277,12 @@ bool ReadHandler::IsFromSubscriber(Messaging::ExchangeContext & apExchangeContex GetAccessingFabricIndex() == apExchangeContext.GetSessionHandle()->GetFabricIndex()); } -CHIP_ERROR ReadHandler::OnUnknownMsgType(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload) +CHIP_ERROR ReadHandler::OnUnknownMsgType() { - ChipLogDetail(DataManagement, "Msg type %d not supported", aPayloadHeader.GetMessageType()); + CHIP_ERROR err = + StatusResponse::Send(Protocols::InteractionModel::Status::InvalidAction, mExchangeCtx.Get(), false /*aExpectResponse*/); Close(); - return CHIP_ERROR_INVALID_MESSAGE_TYPE; + return err; } void ReadHandler::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 548df710a96b2c..18ada21bab332b 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -373,8 +373,7 @@ class ReadHandler : public Messaging::ExchangeDelegate CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) override; void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override; - CHIP_ERROR OnUnknownMsgType(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, - System::PacketBufferHandle && aPayload); + CHIP_ERROR OnUnknownMsgType(); void MoveToState(const HandlerState aTargetState); const char * GetStateStr() const; diff --git a/src/app/StatusResponse.cpp b/src/app/StatusResponse.cpp index e9e8af70a5f656..c6ed1cbfaf57b4 100644 --- a/src/app/StatusResponse.cpp +++ b/src/app/StatusResponse.cpp @@ -44,7 +44,7 @@ CHIP_ERROR StatusResponse::Send(Protocols::InteractionModel::Status aStatus, Mes return CHIP_NO_ERROR; } -CHIP_ERROR StatusResponse::ProcessStatusResponse(System::PacketBufferHandle && aPayload) +CHIP_ERROR StatusResponse::ProcessStatusResponse(System::PacketBufferHandle && aPayload, CHIP_ERROR & aStatusError) { StatusResponseMessage::Parser response; System::PacketBufferTLVReader reader; @@ -59,12 +59,8 @@ CHIP_ERROR StatusResponse::ProcessStatusResponse(System::PacketBufferHandle && a ChipLogValueIMStatus(status.mStatus)); ReturnErrorOnFailure(response.ExitContainer()); - if (status.mStatus == Protocols::InteractionModel::Status::Success) - { - return CHIP_NO_ERROR; - } - - return status.ToChipError(); + aStatusError = status.ToChipError(); + return CHIP_NO_ERROR; } } // namespace app } // namespace chip diff --git a/src/app/StatusResponse.h b/src/app/StatusResponse.h index 583cbb3dae8120..7021e3868bcb5a 100644 --- a/src/app/StatusResponse.h +++ b/src/app/StatusResponse.h @@ -32,7 +32,10 @@ class StatusResponse public: static CHIP_ERROR Send(Protocols::InteractionModel::Status aStatus, Messaging::ExchangeContext * apExchangeContext, bool aExpectResponse); - static CHIP_ERROR ProcessStatusResponse(System::PacketBufferHandle && aPayload); + + // The return value indicates whether the StatusResponse was parsed properly, and if it is CHIP_NO_ERROR + // then aStatus has been set to the actual status, which might be success or failure. + static CHIP_ERROR ProcessStatusResponse(System::PacketBufferHandle && aPayload, CHIP_ERROR & aStatus); }; } // namespace app } // namespace chip diff --git a/src/app/TimedRequest.cpp b/src/app/TimedRequest.cpp index 1c325b62a19187..59921ce17cbc11 100644 --- a/src/app/TimedRequest.cpp +++ b/src/app/TimedRequest.cpp @@ -53,12 +53,5 @@ CHIP_ERROR TimedRequest::Send(ExchangeContext * aExchangeContext, uint16_t aTime return aExchangeContext->SendMessage(MsgType::TimedRequest, std::move(payload), SendMessageFlags::kExpectResponse); } -CHIP_ERROR TimedRequest::HandleResponse(const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) -{ - VerifyOrReturnError(aPayloadHeader.HasMessageType(MsgType::StatusResponse), CHIP_ERROR_INVALID_MESSAGE_TYPE); - - return StatusResponse::ProcessStatusResponse(std::move(aPayload)); -} - } // namespace app } // namespace chip diff --git a/src/app/TimedRequest.h b/src/app/TimedRequest.h index edfe1d78cf8b6a..d8cb67a4768974 100644 --- a/src/app/TimedRequest.h +++ b/src/app/TimedRequest.h @@ -33,14 +33,6 @@ class TimedRequest public: // Send a timed request with the given timeout value on the given exchange. static CHIP_ERROR Send(Messaging::ExchangeContext * aExchangeContext, uint16_t aTimeoutMs); - - // Handle a response message (which may not actually be a StatusResponse, - // but came in after we sent a timed request). - // - // If the response is a failure StatusResponse, its status will be - // encapsulated in the CHIP_ERROR this returns. In that case, - // StatusIB::InitFromChipError can be used to extract the status. - static CHIP_ERROR HandleResponse(const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload); }; } // namespace app diff --git a/src/app/WriteClient.cpp b/src/app/WriteClient.cpp index a56f1d4960f9cf..ccaf6d10ecdf5c 100644 --- a/src/app/WriteClient.cpp +++ b/src/app/WriteClient.cpp @@ -414,6 +414,7 @@ CHIP_ERROR WriteClient::SendWriteRequest() CHIP_ERROR WriteClient::OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) { + bool suppressErrorStatusResponse = false; if (mState == State::AwaitingResponse && // We had sent the last chunk of data, and received all responses mChunks.IsNull()) @@ -431,7 +432,18 @@ CHIP_ERROR WriteClient::OnMessageReceived(Messaging::ExchangeContext * apExchang if (mState == State::AwaitingTimedStatus) { - err = HandleTimedStatus(aPayloadHeader, std::move(aPayload)); + if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse)) + { + CHIP_ERROR statusError = CHIP_NO_ERROR; + SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError)); + suppressErrorStatusResponse = true; + SuccessOrExit(err = statusError); + err = SendWriteRequest(); + } + else + { + err = CHIP_ERROR_INVALID_MESSAGE_TYPE; + } // Skip all other processing here (which is for the response to the // write request), no matter whether err is success or not. goto exit; @@ -449,8 +461,10 @@ CHIP_ERROR WriteClient::OnMessageReceived(Messaging::ExchangeContext * apExchang } else if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse)) { - err = StatusResponse::ProcessStatusResponse(std::move(aPayload)); - SuccessOrExit(err); + CHIP_ERROR statusError = CHIP_NO_ERROR; + SuccessOrExit(err = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError)); + SuccessOrExit(err = statusError); + err = CHIP_ERROR_INVALID_MESSAGE_TYPE; } else { @@ -458,11 +472,22 @@ CHIP_ERROR WriteClient::OnMessageReceived(Messaging::ExchangeContext * apExchang } exit: - if (mpCallback != nullptr) + ResponseMessageHandled(err, apExchangeContext, suppressErrorStatusResponse); + return err; +} + +void WriteClient::ResponseMessageHandled(CHIP_ERROR aError, Messaging::ExchangeContext * apExchangeContext, + bool aSuppressErrorStatusResponse) +{ + if (aError != CHIP_NO_ERROR) { - if (err != CHIP_NO_ERROR) + StatusResponse::Send(Protocols::InteractionModel::Status::InvalidAction, apExchangeContext, false /*aExpectResponse*/); + if (!aSuppressErrorStatusResponse) { - mpCallback->OnError(this, err); + if (mpCallback != nullptr) + { + mpCallback->OnError(this, aError); + } } } @@ -470,9 +495,6 @@ CHIP_ERROR WriteClient::OnMessageReceived(Messaging::ExchangeContext * apExchang { Close(); } - // Else we got a response to a Timed Request and just sent the write. - - return err; } void WriteClient::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) @@ -522,12 +544,5 @@ CHIP_ERROR WriteClient::ProcessAttributeStatusIB(AttributeStatusIB::Parser & aAt return err; } -CHIP_ERROR WriteClient::HandleTimedStatus(const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) -{ - ReturnErrorOnFailure(TimedRequest::HandleResponse(aPayloadHeader, std::move(aPayload))); - - return SendWriteRequest(); -} - } // namespace app } // namespace chip diff --git a/src/app/WriteClient.h b/src/app/WriteClient.h index 395809dc6cb750..389fb93e51b1d2 100644 --- a/src/app/WriteClient.h +++ b/src/app/WriteClient.h @@ -340,15 +340,6 @@ class WriteClient : public Messaging::ExchangeDelegate */ void Abort(); - // Handle a message received when we are expecting a status response to a - // Timed Request. The caller is assumed to have already checked that our - // exchange context member is the one the message came in on. - // - // If the server returned an error status response its status will be - // encapsulated in the CHIP_ERROR this returns. In that case, - // StatusIB::InitFromChipError can be used to extract the status. - CHIP_ERROR HandleTimedStatus(const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload); - // Send our queued-up Write Request message. Assumes the exchange is ready // and mPendingWriteData is populated. CHIP_ERROR SendWriteRequest(); @@ -360,6 +351,8 @@ class WriteClient : public Messaging::ExchangeDelegate CHIP_ERROR FinishAttributeIB(); TLV::TLVWriter * GetAttributeDataIBTLVWriter(); + void ResponseMessageHandled(CHIP_ERROR aError, Messaging::ExchangeContext * apExchangeContext, + bool aSuppressErrorStatusResponse); /** * Create a new message (or a new chunk) for the write request. */ diff --git a/src/app/WriteHandler.cpp b/src/app/WriteHandler.cpp index 12a2086e7c48d3..841c5c232a8b64 100644 --- a/src/app/WriteHandler.cpp +++ b/src/app/WriteHandler.cpp @@ -31,7 +31,7 @@ namespace chip { namespace app { using namespace Protocols::InteractionModel; - +using Status = Protocols::InteractionModel::Status; constexpr uint8_t kListAttributeType = 0x48; CHIP_ERROR WriteHandler::Init() @@ -63,7 +63,7 @@ Status WriteHandler::HandleWriteRequestMessage(Messaging::ExchangeContext * apEx System::PacketBufferHandle && aPayload, bool aIsTimedWrite) { System::PacketBufferHandle packet = System::PacketBufferHandle::New(chip::app::kMaxSecureSduLengthBytes); - VerifyOrReturnError(!packet.IsNull(), Status::Failure); + VerifyOrReturnError(!packet.IsNull(), Status::ResourceExhausted); System::PacketBufferTLVWriter messageWriter; messageWriter.Init(std::move(packet)); @@ -116,11 +116,13 @@ CHIP_ERROR WriteHandler::OnMessageReceived(Messaging::ExchangeContext * apExchan "Incoming exchange context should be same as the initial request."); VerifyOrDieWithMsg(!apExchangeContext->IsGroupExchangeContext(), DataManagement, "OnMessageReceived should not be called on GroupExchangeContext"); + if (!aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::WriteRequest)) { ChipLogDetail(DataManagement, "Unexpected message type %d", aPayloadHeader.GetMessageType()); + err = StatusResponse::Send(Status::InvalidAction, apExchangeContext, false /*aExpectResponse*/); Close(); - return CHIP_ERROR_INVALID_MESSAGE_TYPE; + return err; } Status status = @@ -133,12 +135,12 @@ CHIP_ERROR WriteHandler::OnMessageReceived(Messaging::ExchangeContext * apExchan Close(); } } - else if (status != Protocols::InteractionModel::Status::Success) + else { err = StatusResponse::Send(status, apExchangeContext, false /*aExpectResponse*/); Close(); } - return CHIP_NO_ERROR; + return err; } void WriteHandler::OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) @@ -312,7 +314,7 @@ CHIP_ERROR WriteHandler::ProcessAttributeDataIBs(TLV::TLVReader & aAttributeData // it with Busy status code. (dataAttributePath.IsListItemOperation() && !IsSameAttribute(mProcessingAttributePath, dataAttributePath))) { - err = AddStatus(dataAttributePath, StatusIB(Protocols::InteractionModel::Status::Busy)); + err = AddStatus(dataAttributePath, StatusIB(Status::Busy)); continue; } @@ -612,20 +614,18 @@ Status WriteHandler::ProcessWriteRequest(System::PacketBufferHandle && aPayload, return status; } -CHIP_ERROR WriteHandler::AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::Status aStatus) +CHIP_ERROR WriteHandler::AddStatus(const ConcreteDataAttributePath & aPath, const Status aStatus) { return AddStatus(aPath, StatusIB(aStatus)); } CHIP_ERROR WriteHandler::AddClusterSpecificSuccess(const ConcreteDataAttributePath & aPath, ClusterStatus aClusterStatus) { - using Protocols::InteractionModel::Status; return AddStatus(aPath, StatusIB(Status::Success, aClusterStatus)); } CHIP_ERROR WriteHandler::AddClusterSpecificFailure(const ConcreteDataAttributePath & aPath, ClusterStatus aClusterStatus) { - using Protocols::InteractionModel::Status; return AddStatus(aPath, StatusIB(Status::Failure, aClusterStatus)); } @@ -684,7 +684,7 @@ const char * WriteHandler::GetStateStr() const void WriteHandler::MoveToState(const State aTargetState) { mState = aTargetState; - ChipLogDetail(DataManagement, "IM WH moving to [%s]", GetStateStr()); + ChipLogDetail(DataManagement, "IM WriteHandler moving to [%s]", GetStateStr()); } void WriteHandler::ClearState() diff --git a/src/app/WriteHandler.h b/src/app/WriteHandler.h index 43163a2b8abab9..f1017b264f47c5 100644 --- a/src/app/WriteHandler.h +++ b/src/app/WriteHandler.h @@ -121,9 +121,10 @@ class WriteHandler : public Messaging::ExchangeDelegate AddStatus, // The handler has added status code Sending, // The handler has sent out the write response }; - Protocols::InteractionModel::Status ProcessWriteRequest(System::PacketBufferHandle && aPayload, bool aIsTimedWrite); - Protocols::InteractionModel::Status HandleWriteRequestMessage(Messaging::ExchangeContext * apExchangeContext, - System::PacketBufferHandle && aPayload, bool aIsTimedWrite); + using Status = Protocols::InteractionModel::Status; + Status ProcessWriteRequest(System::PacketBufferHandle && aPayload, bool aIsTimedWrite); + Status HandleWriteRequestMessage(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload, + bool aIsTimedWrite); CHIP_ERROR FinalizeMessage(System::PacketBufferTLVWriter && aMessageWriter, System::PacketBufferHandle & packet); CHIP_ERROR SendWriteResponse(System::PacketBufferTLVWriter && aMessageWriter); diff --git a/src/app/tests/TestCommandInteraction.cpp b/src/app/tests/TestCommandInteraction.cpp index 41245dca9be0a5..043e4ca91241f2 100644 --- a/src/app/tests/TestCommandInteraction.cpp +++ b/src/app/tests/TestCommandInteraction.cpp @@ -40,13 +40,12 @@ #include #include #include +#include #include #include #include #include -#include - using TestContext = chip::Test::AppContext; using namespace chip::Protocols; @@ -215,6 +214,8 @@ class TestCommandInteraction static void TestCommandHandlerWithSendSimpleCommandData(nlTestSuite * apSuite, void * apContext); static void TestCommandHandlerCommandDataEncoding(nlTestSuite * apSuite, void * apContext); static void TestCommandHandlerCommandEncodeFailure(nlTestSuite * apSuite, void * apContext); + static void TestCommandInvalidMessage1(nlTestSuite * apSuite, void * apContext); + static void TestCommandInvalidMessage2(nlTestSuite * apSuite, void * apContext); static void TestCommandHandlerCommandEncodeExternalFailure(nlTestSuite * apSuite, void * apContext); static void TestCommandHandlerWithSendSimpleStatusCode(nlTestSuite * apSuite, void * apContext); static void TestCommandHandlerWithSendEmptyResponse(nlTestSuite * apSuite, void * apContext); @@ -247,6 +248,9 @@ class TestCommandInteraction EndpointId aEndpointId = kTestEndpointId); static void AddInvokeRequestData(nlTestSuite * apSuite, void * apContext, CommandSender * apCommandSender, CommandId aCommandId = kTestCommandIdWithData); + + static void AddInvalidInvokeRequestData(nlTestSuite * apSuite, void * apContext, CommandSender * apCommandSender, + CommandId aCommandId = kTestCommandIdWithData); static void AddInvokeResponseData(nlTestSuite * apSuite, void * apContext, CommandHandler * apCommandHandler, bool aNeedStatusCode, CommandId aCommandId = kTestCommandIdWithData); static void ValidateCommandHandlerWithSendCommand(nlTestSuite * apSuite, void * apContext, bool aNeedStatusCode); @@ -399,6 +403,23 @@ void TestCommandInteraction::AddInvokeRequestData(nlTestSuite * apSuite, void * NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); } +void TestCommandInteraction::AddInvalidInvokeRequestData(nlTestSuite * apSuite, void * apContext, CommandSender * apCommandSender, + CommandId aCommandId) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + auto commandPathParams = MakeTestCommandPath(aCommandId); + + err = apCommandSender->PrepareCommand(commandPathParams); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + chip::TLV::TLVWriter * writer = apCommandSender->GetCommandDataIBTLVWriter(); + + err = writer->PutBoolean(chip::TLV::ContextTag(1), true); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + apCommandSender->MoveToState(CommandSender::State::AddedCommand); +} + void TestCommandInteraction::AddInvokeResponseData(nlTestSuite * apSuite, void * apContext, CommandHandler * apCommandHandler, bool aNeedStatusCode, CommandId aCommandId) { @@ -627,6 +648,89 @@ void TestCommandInteraction::TestCommandHandlerCommandEncodeFailure(nlTestSuite #endif } +// Command Sender sends the invoke request, command handler drops invoke response, then test injects unknown message to client, +// client would out the status response with invalid action, and interaction model engine would send out the status reponse with +// invalid action +void TestCommandInteraction::TestCommandInvalidMessage1(nlTestSuite * apSuite, void * apContext) +{ + TestContext & ctx = *static_cast(apContext); + CHIP_ERROR err = CHIP_NO_ERROR; + mockCommandSenderDelegate.ResetCounter(); + app::CommandSender commandSender(&mockCommandSenderDelegate, &ctx.GetExchangeManager()); + + AddInvokeRequestData(apSuite, apContext, &commandSender); + asyncCommand = false; + + ctx.GetLoopback().mSentMessageCount = 0; + ctx.GetLoopback().mNumMessagesToDrop = 1; + ctx.GetLoopback().mNumMessagesToDropSinceIndex = 2; + err = commandSender.SendCommandRequest(ctx.GetSessionBobToAlice()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + ctx.DrainAndServiceIO(); + + NL_TEST_ASSERT(apSuite, + mockCommandSenderDelegate.onResponseCalledTimes == 0 && mockCommandSenderDelegate.onFinalCalledTimes == 0 && + mockCommandSenderDelegate.onErrorCalledTimes == 0); + + NL_TEST_ASSERT(apSuite, GetNumActiveHandlerObjects() == 0); + + System::PacketBufferHandle msgBuf = System::PacketBufferHandle::New(kMaxSecureSduLengthBytes); + NL_TEST_ASSERT(apSuite, !msgBuf.IsNull()); + System::PacketBufferTLVWriter writer; + writer.Init(std::move(msgBuf)); + StatusResponseMessage::Builder response; + response.Init(&writer); + response.Status(Protocols::InteractionModel::Status::InvalidAction); + NL_TEST_ASSERT(apSuite, writer.Finalize(&msgBuf) == CHIP_NO_ERROR); + + PayloadHeader payloadHeader; + payloadHeader.SetExchangeID(0); + payloadHeader.SetMessageType(chip::Protocols::InteractionModel::MsgType::StatusResponse); + + Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + rm->ClearRetransTable(commandSender.mExchangeCtx.Get()); + ctx.GetLoopback().mSentMessageCount = 0; + ctx.GetLoopback().mNumMessagesToDrop = 0; + ctx.GetLoopback().mNumMessagesToDropSinceIndex = 0; + + err = commandSender.OnMessageReceived(commandSender.mExchangeCtx.Get(), payloadHeader, std::move(msgBuf)); + NL_TEST_ASSERT(apSuite, err != CHIP_NO_ERROR); + NL_TEST_ASSERT(apSuite, + mockCommandSenderDelegate.onResponseCalledTimes == 0 && mockCommandSenderDelegate.onFinalCalledTimes == 1 && + mockCommandSenderDelegate.onErrorCalledTimes == 1); + + ctx.DrainAndServiceIO(); + NL_TEST_ASSERT(apSuite, ctx.GetLoopback().mSentMessageCount == 2); + NL_TEST_ASSERT(apSuite, GetNumActiveHandlerObjects() == 0); + ctx.ExpireSessionAliceToBob(); + ctx.ExpireSessionBobToAlice(); + ctx.CreateSessionAliceToBob(); + ctx.CreateSessionBobToAlice(); +} + +// Command Sender sends the malformed invoke request, handler fails to process it and send status report with invalid action +void TestCommandInteraction::TestCommandInvalidMessage2(nlTestSuite * apSuite, void * apContext) +{ + TestContext & ctx = *static_cast(apContext); + CHIP_ERROR err = CHIP_NO_ERROR; + + mockCommandSenderDelegate.ResetCounter(); + app::CommandSender commandSender(&mockCommandSenderDelegate, &ctx.GetExchangeManager()); + + AddInvalidInvokeRequestData(apSuite, apContext, &commandSender); + err = commandSender.SendCommandRequest(ctx.GetSessionBobToAlice()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + ctx.DrainAndServiceIO(); + + NL_TEST_ASSERT(apSuite, + mockCommandSenderDelegate.onResponseCalledTimes == 0 && mockCommandSenderDelegate.onFinalCalledTimes == 1 && + mockCommandSenderDelegate.onErrorCalledTimes == 1); + + NL_TEST_ASSERT(apSuite, GetNumActiveHandlerObjects() == 0); + NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); +} + void TestCommandInteraction::TestCommandHandlerCommandEncodeExternalFailure(nlTestSuite * apSuite, void * apContext) { TestContext & ctx = *static_cast(apContext); @@ -667,7 +771,6 @@ void TestCommandInteraction::TestCommandHandlerWithSendSimpleStatusCode(nlTestSu void TestCommandInteraction::TestCommandHandlerWithProcessReceivedMsg(nlTestSuite * apSuite, void * apContext) { TestContext & ctx = *static_cast(apContext); - CHIP_ERROR err = CHIP_NO_ERROR; app::CommandHandler commandHandler(&mockCommandHandlerDelegate); System::PacketBufferHandle commandDatabuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); @@ -675,18 +778,18 @@ void TestCommandInteraction::TestCommandHandlerWithProcessReceivedMsg(nlTestSuit commandHandler.mExchangeCtx.Grab(ctx.NewExchangeToAlice(&delegate)); GenerateInvokeRequest(apSuite, apContext, commandDatabuf, /* aIsTimedRequest = */ false, kTestCommandIdWithData); - err = commandHandler.ProcessInvokeRequest(std::move(commandDatabuf), false); + Protocols::InteractionModel::Status status = commandHandler.ProcessInvokeRequest(std::move(commandDatabuf), false); - ChipLogDetail(DataManagement, "###################################### %s", err.AsString()); - NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(apSuite, status == Protocols::InteractionModel::Status::Success); } void TestCommandInteraction::TestCommandHandlerWithProcessReceivedNotExistCommand(nlTestSuite * apSuite, void * apContext) { - CHIP_ERROR err = CHIP_NO_ERROR; + TestContext & ctx = *static_cast(apContext); app::CommandHandler commandHandler(&mockCommandHandlerDelegate); System::PacketBufferHandle commandDatabuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); - + TestExchangeDelegate delegate; + commandHandler.mExchangeCtx.Grab(ctx.NewExchangeToAlice(&delegate)); // Use some invalid endpoint / cluster / command. GenerateInvokeRequest(apSuite, apContext, commandDatabuf, /* aIsTimedRequest = */ false, 0xEF /* command */, 0xADBE /* cluster */, 0xDE /* endpoint */); @@ -694,7 +797,7 @@ void TestCommandInteraction::TestCommandHandlerWithProcessReceivedNotExistComman // TODO: Need to find a way to get the response instead of only check if a function on key path is called. // We should not reach CommandDispatch if requested command does not exist. chip::isCommandDispatched = false; - err = commandHandler.ProcessInvokeRequest(std::move(commandDatabuf), false); + commandHandler.ProcessInvokeRequest(std::move(commandDatabuf), false); NL_TEST_ASSERT(apSuite, !chip::isCommandDispatched); } @@ -706,7 +809,6 @@ void TestCommandInteraction::TestCommandHandlerWithProcessReceivedEmptyDataMsg(n { for (auto transactionIsTimed : allBooleans) { - CHIP_ERROR err = CHIP_NO_ERROR; app::CommandHandler commandHandler(&mockCommandHandlerDelegate); System::PacketBufferHandle commandDatabuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); @@ -714,9 +816,10 @@ void TestCommandInteraction::TestCommandHandlerWithProcessReceivedEmptyDataMsg(n commandHandler.mExchangeCtx.Grab(ctx.NewExchangeToAlice(&delegate)); chip::isCommandDispatched = false; + GenerateInvokeRequest(apSuite, apContext, commandDatabuf, messageIsTimed, kTestCommandIdNoData); - err = commandHandler.ProcessInvokeRequest(std::move(commandDatabuf), transactionIsTimed); - NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + commandHandler.ProcessInvokeRequest(std::move(commandDatabuf), transactionIsTimed); + NL_TEST_ASSERT(apSuite, chip::isCommandDispatched == (messageIsTimed == transactionIsTimed)); } } @@ -934,6 +1037,8 @@ namespace { // clang-format off const nlTest sTests[] = { + NL_TEST_DEF("TestCommandInvalidMessage1", chip::app::TestCommandInteraction::TestCommandInvalidMessage1), + NL_TEST_DEF("TestCommandInvalidMessage2", chip::app::TestCommandInteraction::TestCommandInvalidMessage2), NL_TEST_DEF("TestCommandSenderWithWrongState", chip::app::TestCommandInteraction::TestCommandSenderWithWrongState), NL_TEST_DEF("TestCommandHandlerWithWrongState", chip::app::TestCommandInteraction::TestCommandHandlerWithWrongState), NL_TEST_DEF("TestCommandSenderWithSendCommand", chip::app::TestCommandInteraction::TestCommandSenderWithSendCommand), @@ -948,7 +1053,6 @@ const nlTest sTests[] = NL_TEST_DEF("TestCommandHandlerWithProcessReceivedNotExistCommand", chip::app::TestCommandInteraction::TestCommandHandlerWithProcessReceivedNotExistCommand), NL_TEST_DEF("TestCommandHandlerWithProcessReceivedEmptyDataMsg", chip::app::TestCommandInteraction::TestCommandHandlerWithProcessReceivedEmptyDataMsg), NL_TEST_DEF("TestCommandHandlerRejectMultipleCommands", chip::app::TestCommandInteraction::TestCommandHandlerRejectMultipleCommands), - NL_TEST_DEF("TestCommandSenderCommandSuccessResponseFlow", chip::app::TestCommandInteraction::TestCommandSenderCommandSuccessResponseFlow), NL_TEST_DEF("TestCommandSenderCommandAsyncSuccessResponseFlow", chip::app::TestCommandInteraction::TestCommandSenderCommandAsyncSuccessResponseFlow), NL_TEST_DEF("TestCommandSenderCommandSpecificResponseFlow", chip::app::TestCommandInteraction::TestCommandSenderCommandSpecificResponseFlow), diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp index c4aa3fd32fd99e..f6969e3c7ade7a 100644 --- a/src/app/tests/TestReadInteraction.cpp +++ b/src/app/tests/TestReadInteraction.cpp @@ -25,6 +25,7 @@ #include "lib/support/CHIPMem.h" #include #include +#include #include #include #include @@ -182,7 +183,7 @@ class MockInteractionModelApp : public chip::app::ReadClient::Callback mReadError = true; } - void OnDone(chip::app::ReadClient *) override {} + void OnDone(chip::app::ReadClient *) override { ChipLogDetail(DataManagement, "OnDone is called"); } void OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) override { @@ -320,6 +321,11 @@ class TestReadInteraction static void TestSubscribeRoundtripChunkStatusReportTimeout(nlTestSuite * apSuite, void * apContext); static void TestPostSubscribeRoundtripChunkStatusReportTimeout(nlTestSuite * apSuite, void * apContext); static void TestPostSubscribeRoundtripChunkReportTimeout(nlTestSuite * apSuite, void * apContext); + static void TestReadInvalidMessage1(nlTestSuite * apSuite, void * apContext); + static void TestReadInvalidMessage2(nlTestSuite * apSuite, void * apContext); + static void TestSubscribeInvalidMessage1(nlTestSuite * apSuite, void * apContext); + static void TestSubscribeInvalidMessage2(nlTestSuite * apSuite, void * apContext); + static void TestSubscribeInvalidMessage3(nlTestSuite * apSuite, void * apContext); private: static void GenerateReportData(nlTestSuite * apSuite, void * apContext, System::PacketBufferHandle & aPayload, @@ -2719,6 +2725,307 @@ void TestReadInteraction::TestPostSubscribeRoundtripChunkReportTimeout(nlTestSui ctx.CreateSessionBobToAlice(); } + +// Read Client sends the malformed read request, server fails to parse the request and generated the status report to client, and +// client would also be closed. +void TestReadInteraction::TestReadInvalidMessage1(nlTestSuite * apSuite, void * apContext) +{ + TestContext & ctx = *static_cast(apContext); + CHIP_ERROR err = CHIP_NO_ERROR; + + Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + // Shouldn't have anything in the retransmit table when starting the test. + NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); + + MockInteractionModelApp delegate; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); + + { + app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate, + chip::app::ReadClient::InteractionType::Read); + System::PacketBufferHandle msgBuf; + ReadRequestMessage::Builder request; + System::PacketBufferTLVWriter writer; + + chip::app::InitWriterWithSpaceReserved(writer, 0); + err = request.Init(&writer); + err = writer.Finalize(&msgBuf); + + auto exchange = readClient.mpExchangeMgr->NewContext(readPrepareParams.mSessionHolder.Get().Value(), &readClient); + NL_TEST_ASSERT(apSuite, exchange != nullptr); + readClient.mExchange.Grab(exchange); + readClient.MoveToState(app::ReadClient::ClientState::AwaitingInitialReport); + err = readClient.mExchange->SendMessage(Protocols::InteractionModel::MsgType::ReadRequest, std::move(msgBuf), + Messaging::SendFlags(Messaging::SendMessageFlags::kExpectResponse)); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + ctx.DrainAndServiceIO(); + NL_TEST_ASSERT(apSuite, delegate.mError != CHIP_NO_ERROR); + } + NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadClients() == 0); + engine->Shutdown(); + NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); +} + +// Read Client sends the read request, hander drops out the first report, then test asks read client to process invalid message, +// read client sends out status report +void TestReadInteraction::TestReadInvalidMessage2(nlTestSuite * apSuite, void * apContext) +{ + TestContext & ctx = *static_cast(apContext); + CHIP_ERROR err = CHIP_NO_ERROR; + ctx.GetLoopback().mSentMessageCount = 0; + Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + // Shouldn't have anything in the retransmit table when starting the test. + NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); + + MockInteractionModelApp delegate; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); + + chip::app::AttributePathParams attributePathParams[2]; + readPrepareParams.mpAttributePathParamsList = attributePathParams; + readPrepareParams.mpAttributePathParamsList[0].mEndpointId = kTestEndpointId; + readPrepareParams.mpAttributePathParamsList[0].mClusterId = kTestClusterId; + readPrepareParams.mpAttributePathParamsList[0].mAttributeId = 1; + + readPrepareParams.mpAttributePathParamsList[1].mEndpointId = kTestEndpointId; + readPrepareParams.mpAttributePathParamsList[1].mClusterId = kTestClusterId; + readPrepareParams.mpAttributePathParamsList[1].mAttributeId = 2; + + readPrepareParams.mAttributePathParamsListSize = 2; + + { + app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate, + chip::app::ReadClient::InteractionType::Read); + + ctx.GetLoopback().mNumMessagesToDrop = 1; + ctx.GetLoopback().mNumMessagesToDropSinceIndex = 2; + err = readClient.SendRequest(readPrepareParams); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + ctx.DrainAndServiceIO(); + + System::PacketBufferHandle msgBuf = System::PacketBufferHandle::New(kMaxSecureSduLengthBytes); + NL_TEST_ASSERT(apSuite, !msgBuf.IsNull()); + System::PacketBufferTLVWriter writer; + writer.Init(std::move(msgBuf)); + StatusResponseMessage::Builder response; + response.Init(&writer); + response.Status(Protocols::InteractionModel::Status::InvalidAction); + NL_TEST_ASSERT(apSuite, writer.Finalize(&msgBuf) == CHIP_NO_ERROR); + PayloadHeader payloadHeader; + payloadHeader.SetExchangeID(0); + payloadHeader.SetMessageType(chip::Protocols::InteractionModel::MsgType::StatusResponse); + + rm->ClearRetransTable(readClient.mExchange.Get()); + ctx.GetLoopback().mSentMessageCount = 0; + ctx.GetLoopback().mNumMessagesToDrop = 0; + ctx.GetLoopback().mNumMessagesToDropSinceIndex = 0; + readClient.OnMessageReceived(readClient.mExchange.Get(), payloadHeader, std::move(msgBuf)); + ctx.DrainAndServiceIO(); + NL_TEST_ASSERT(apSuite, ctx.GetLoopback().mSentMessageCount == 2); + } + + NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadClients() == 0); + engine->Shutdown(); + ctx.ExpireSessionAliceToBob(); + ctx.ExpireSessionBobToAlice(); + ctx.CreateSessionAliceToBob(); + ctx.CreateSessionBobToAlice(); +} + +// Read Client creates the subscription with server, server sends chunked reports, after the handler sends out the first chunked +// report, handler calls unknown message function, send status report with invalid action and close, client sends the status report +// and close. InteractionModelEngine would process the above status report, and sends out the status report with invalid action. +void TestReadInteraction::TestSubscribeInvalidMessage1(nlTestSuite * apSuite, void * apContext) +{ + TestContext & ctx = *static_cast(apContext); + CHIP_ERROR err = CHIP_NO_ERROR; + + Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + // Shouldn't have anything in the retransmit table when starting the test. + NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); + + MockInteractionModelApp delegate; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + chip::app::AttributePathParams attributePathParams[1]; + // Mock Attribute 4 is a big attribute, with 6 large OCTET_STRING + attributePathParams[0].mEndpointId = Test::kMockEndpoint3; + attributePathParams[0].mClusterId = Test::MockClusterId(2); + attributePathParams[0].mAttributeId = Test::MockAttributeId(4); + + ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); + readPrepareParams.mpAttributePathParamsList = attributePathParams; + readPrepareParams.mAttributePathParamsListSize = 1; + + { + app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate, + chip::app::ReadClient::InteractionType::Subscribe); + + ctx.GetLoopback().mSentMessageCount = 0; + ctx.GetLoopback().mNumMessagesToDrop = 1; + ctx.GetLoopback().mNumMessagesToDropSinceIndex = 2; + + err = readClient.SendRequest(readPrepareParams); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + ctx.DrainAndServiceIO(); + + ctx.GetLoopback().mSentMessageCount = 0; + ctx.GetLoopback().mNumMessagesToDrop = 0; + ctx.GetLoopback().mNumMessagesToDropSinceIndex = 0; + + rm->ClearRetransTable(readClient.mExchange.Get()); + NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers() == 1); + NL_TEST_ASSERT(apSuite, engine->ActiveHandlerAt(0) != nullptr); + rm->ClearRetransTable(engine->ActiveHandlerAt(0)->mExchangeCtx.Get()); + + System::PacketBufferHandle msgBuf; + ReadRequestMessage::Builder request; + System::PacketBufferTLVWriter writer; + chip::app::InitWriterWithSpaceReserved(writer, 0); + request.Init(&writer); + writer.Finalize(&msgBuf); + + err = readClient.mExchange->SendMessage(Protocols::InteractionModel::MsgType::WriteRequest, std::move(msgBuf)); + ctx.DrainAndServiceIO(); + NL_TEST_ASSERT(apSuite, ctx.GetLoopback().mSentMessageCount == 4); + } + + NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadClients() == 0); + engine->Shutdown(); + ctx.ExpireSessionAliceToBob(); + ctx.ExpireSessionBobToAlice(); + ctx.CreateSessionAliceToBob(); + ctx.CreateSessionBobToAlice(); +} + +// Read Client sends the malformed subscribe request, server fails to parse the request and generated the status report to client, +// and client would also be closed. +void TestReadInteraction::TestSubscribeInvalidMessage2(nlTestSuite * apSuite, void * apContext) +{ + TestContext & ctx = *static_cast(apContext); + CHIP_ERROR err = CHIP_NO_ERROR; + + Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + // Shouldn't have anything in the retransmit table when starting the test. + NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); + + MockInteractionModelApp delegate; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); + + { + app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate, + chip::app::ReadClient::InteractionType::Subscribe); + System::PacketBufferHandle msgBuf; + ReadRequestMessage::Builder request; + System::PacketBufferTLVWriter writer; + + chip::app::InitWriterWithSpaceReserved(writer, 0); + err = request.Init(&writer); + err = writer.Finalize(&msgBuf); + + auto exchange = readClient.mpExchangeMgr->NewContext(readPrepareParams.mSessionHolder.Get().Value(), &readClient); + NL_TEST_ASSERT(apSuite, exchange != nullptr); + readClient.mExchange.Grab(exchange); + readClient.MoveToState(app::ReadClient::ClientState::AwaitingInitialReport); + err = readClient.mExchange->SendMessage(Protocols::InteractionModel::MsgType::SubscribeRequest, std::move(msgBuf), + Messaging::SendFlags(Messaging::SendMessageFlags::kExpectResponse)); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + ctx.DrainAndServiceIO(); + NL_TEST_ASSERT(apSuite, delegate.mError != CHIP_NO_ERROR); + } + NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadClients() == 0); + engine->Shutdown(); + NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); +} + +// Read Client sends the subscribe request, Read Handler drop the response, then test injects unknown message for Read Client. +void TestReadInteraction::TestSubscribeInvalidMessage3(nlTestSuite * apSuite, void * apContext) +{ + TestContext & ctx = *static_cast(apContext); + CHIP_ERROR err = CHIP_NO_ERROR; + + Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + // Shouldn't have anything in the retransmit table when starting the test. + NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); + + MockInteractionModelApp delegate; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + ReadPrepareParams readPrepareParams(ctx.GetSessionBobToAlice()); + + chip::app::AttributePathParams attributePathParams[2]; + readPrepareParams.mpAttributePathParamsList = attributePathParams; + readPrepareParams.mpAttributePathParamsList[0].mEndpointId = kTestEndpointId; + readPrepareParams.mpAttributePathParamsList[0].mClusterId = kTestClusterId; + readPrepareParams.mpAttributePathParamsList[0].mAttributeId = 1; + + readPrepareParams.mpAttributePathParamsList[1].mEndpointId = kTestEndpointId; + readPrepareParams.mpAttributePathParamsList[1].mClusterId = kTestClusterId; + readPrepareParams.mpAttributePathParamsList[1].mAttributeId = 2; + + readPrepareParams.mAttributePathParamsListSize = 2; + + readPrepareParams.mMinIntervalFloorSeconds = 2; + readPrepareParams.mMaxIntervalCeilingSeconds = 5; + + { + app::ReadClient readClient(chip::app::InteractionModelEngine::GetInstance(), &ctx.GetExchangeManager(), delegate, + chip::app::ReadClient::InteractionType::Subscribe); + + ctx.GetLoopback().mSentMessageCount = 0; + ctx.GetLoopback().mNumMessagesToDrop = 1; + ctx.GetLoopback().mNumMessagesToDropSinceIndex = 2; + err = readClient.SendRequest(readPrepareParams); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + ctx.DrainAndServiceIO(); + + System::PacketBufferHandle msgBuf = System::PacketBufferHandle::New(kMaxSecureSduLengthBytes); + NL_TEST_ASSERT(apSuite, !msgBuf.IsNull()); + System::PacketBufferTLVWriter writer; + writer.Init(std::move(msgBuf)); + StatusResponseMessage::Builder response; + response.Init(&writer); + response.Status(Protocols::InteractionModel::Status::InvalidAction); + NL_TEST_ASSERT(apSuite, writer.Finalize(&msgBuf) == CHIP_NO_ERROR); + PayloadHeader payloadHeader; + payloadHeader.SetExchangeID(0); + payloadHeader.SetMessageType(chip::Protocols::InteractionModel::MsgType::StatusResponse); + + rm->ClearRetransTable(readClient.mExchange.Get()); + NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadHandlers() == 1); + NL_TEST_ASSERT(apSuite, engine->ActiveHandlerAt(0) != nullptr); + rm->ClearRetransTable(engine->ActiveHandlerAt(0)->mExchangeCtx.Get()); + ctx.GetLoopback().mSentMessageCount = 0; + ctx.GetLoopback().mNumMessagesToDrop = 0; + ctx.GetLoopback().mNumMessagesToDropSinceIndex = 0; + readClient.OnMessageReceived(readClient.mExchange.Get(), payloadHeader, std::move(msgBuf)); + ctx.DrainAndServiceIO(); + NL_TEST_ASSERT(apSuite, ctx.GetLoopback().mSentMessageCount == 2); + } + + NL_TEST_ASSERT(apSuite, engine->GetNumActiveReadClients() == 0); + engine->Shutdown(); + ctx.ExpireSessionAliceToBob(); + ctx.ExpireSessionBobToAlice(); + ctx.CreateSessionAliceToBob(); + ctx.CreateSessionBobToAlice(); +} + } // namespace app } // namespace chip @@ -2752,7 +3059,11 @@ const nlTest sTests[] = NL_TEST_DEF("TestReadHandlerInvalidAttributePath", chip::app::TestReadInteraction::TestReadHandlerInvalidAttributePath), NL_TEST_DEF("TestProcessSubscribeRequest", chip::app::TestReadInteraction::TestProcessSubscribeRequest), NL_TEST_DEF("TestSubscribeRoundtrip", chip::app::TestReadInteraction::TestSubscribeRoundtrip), - + NL_TEST_DEF("TestReadInvalidMessage1", chip::app::TestReadInteraction::TestReadInvalidMessage1), + NL_TEST_DEF("TestReadInvalidMessage2", chip::app::TestReadInteraction::TestReadInvalidMessage2), + NL_TEST_DEF("TestSubscribeInvalidMessage1", chip::app::TestReadInteraction::TestSubscribeInvalidMessage1), + NL_TEST_DEF("TestSubscribeInvalidMessage2", chip::app::TestReadInteraction::TestSubscribeInvalidMessage2), + NL_TEST_DEF("TestSubscribeInvalidMessage3", chip::app::TestReadInteraction::TestSubscribeInvalidMessage3), NL_TEST_DEF("TestSubscribeUrgentWildcardEvent", chip::app::TestReadInteraction::TestSubscribeUrgentWildcardEvent), NL_TEST_DEF("TestSubscribeWildcard", chip::app::TestReadInteraction::TestSubscribeWildcard), NL_TEST_DEF("TestSubscribePartialOverlap", chip::app::TestReadInteraction::TestSubscribePartialOverlap), diff --git a/src/app/tests/TestTimedHandler.cpp b/src/app/tests/TestTimedHandler.cpp index e460d10d4a4dfe..6c081e27428a94 100644 --- a/src/app/tests/TestTimedHandler.cpp +++ b/src/app/tests/TestTimedHandler.cpp @@ -68,7 +68,12 @@ class TestExchangeDelegate : public Messaging::ExchangeDelegate mLastMessageWasStatus = aPayloadHeader.HasMessageType(MsgType::StatusResponse); if (mLastMessageWasStatus) { - mError = StatusResponse::ProcessStatusResponse(std::move(aPayload)); + CHIP_ERROR statusError = CHIP_NO_ERROR; + mError = StatusResponse::ProcessStatusResponse(std::move(aPayload), statusError); + if (mError == CHIP_NO_ERROR) + { + mError = statusError; + } } if (mKeepExchangeOpen) { diff --git a/src/app/tests/TestWriteInteraction.cpp b/src/app/tests/TestWriteInteraction.cpp index f1af80cef330b7..c36625f3265458 100644 --- a/src/app/tests/TestWriteInteraction.cpp +++ b/src/app/tests/TestWriteInteraction.cpp @@ -58,6 +58,8 @@ class TestWriteInteraction static void TestWriteClientGroup(nlTestSuite * apSuite, void * apContext); static void TestWriteHandler(nlTestSuite * apSuite, void * apContext); static void TestWriteRoundtrip(nlTestSuite * apSuite, void * apContext); + static void TestWriteInvalidMessage1(nlTestSuite * apSuite, void * apContext); + static void TestWriteInvalidMessage2(nlTestSuite * apSuite, void * apContext); static void TestWriteRoundtripWithClusterObjects(nlTestSuite * apSuite, void * apContext); static void TestWriteRoundtripWithClusterObjectsVersionMatch(nlTestSuite * apSuite, void * apContext); static void TestWriteRoundtripWithClusterObjectsVersionMismatch(nlTestSuite * apSuite, void * apContext); @@ -543,6 +545,98 @@ void TestWriteInteraction::TestWriteRoundtrip(nlTestSuite * apSuite, void * apCo engine->Shutdown(); } +// Write Client sends the write request, and process the unknown message error via OnMessageReceived to close the client. +void TestWriteInteraction::TestWriteInvalidMessage1(nlTestSuite * apSuite, void * apContext) +{ + TestContext & ctx = *static_cast(apContext); + + CHIP_ERROR err = CHIP_NO_ERROR; + + Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + // Shouldn't have anything in the retransmit table when starting the test. + NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); + + TestWriteClientCallback callback; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + app::WriteClient writeClient(engine->GetExchangeManager(), &callback, Optional::Missing()); + + System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); + AddAttributeDataIB(apSuite, apContext, writeClient); + + NL_TEST_ASSERT(apSuite, callback.mOnSuccessCalled == 0 && callback.mOnErrorCalled == 0 && callback.mOnDoneCalled == 0); + + ctx.GetLoopback().mSentMessageCount = 0; + ctx.GetLoopback().mNumMessagesToDrop = 1; + ctx.GetLoopback().mNumMessagesToDropSinceIndex = 2; + err = writeClient.SendWriteRequest(ctx.GetSessionBobToAlice()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + ctx.DrainAndServiceIO(); + + char PAYLOAD[] = "Hello!"; + uint16_t payload_len = sizeof(PAYLOAD); + PayloadHeader payloadHeader; + payloadHeader.SetExchangeID(0); + payloadHeader.SetMessageType(chip::Protocols::InteractionModel::MsgType::ReportData); + chip::System::PacketBufferHandle payload = chip::MessagePacketBuffer::NewWithData(PAYLOAD, payload_len); + NL_TEST_ASSERT(apSuite, !payload.IsNull()); + + rm->ClearRetransTable(writeClient.mExchangeCtx.Get()); + ctx.GetLoopback().mSentMessageCount = 0; + ctx.GetLoopback().mNumMessagesToDrop = 0; + ctx.GetLoopback().mNumMessagesToDropSinceIndex = 0; + err = writeClient.OnMessageReceived(writeClient.mExchangeCtx.Get(), payloadHeader, std::move(payload)); + NL_TEST_ASSERT(apSuite, err != CHIP_NO_ERROR); + ctx.DrainAndServiceIO(); + + NL_TEST_ASSERT(apSuite, callback.mOnSuccessCalled == 0 && callback.mOnErrorCalled == 1 && callback.mOnDoneCalled == 1); + NL_TEST_ASSERT(apSuite, ctx.GetLoopback().mSentMessageCount == 2); + + engine->Shutdown(); + ctx.ExpireSessionAliceToBob(); + ctx.ExpireSessionBobToAlice(); + ctx.CreateSessionAliceToBob(); + ctx.CreateSessionBobToAlice(); +} + +// Write Client sends the malformed write request, and server generates invalid action status report to client, and further the +// client is closed +void TestWriteInteraction::TestWriteInvalidMessage2(nlTestSuite * apSuite, void * apContext) +{ + TestContext & ctx = *static_cast(apContext); + + CHIP_ERROR err = CHIP_NO_ERROR; + + Messaging::ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + // Shouldn't have anything in the retransmit table when starting the test. + NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); + + TestWriteClientCallback callback; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + app::WriteClient writeClient(engine->GetExchangeManager(), &callback, Optional::Missing()); + + NL_TEST_ASSERT(apSuite, callback.mOnSuccessCalled == 0 && callback.mOnErrorCalled == 0 && callback.mOnDoneCalled == 0); + writeClient.StartNewMessage(); + writeClient.MoveToState(WriteClient::State::AddAttribute); + err = writeClient.SendWriteRequest(ctx.GetSessionBobToAlice()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + ctx.DrainAndServiceIO(); + + NL_TEST_ASSERT(apSuite, callback.mOnSuccessCalled == 0 && callback.mOnErrorCalled == 1 && callback.mOnDoneCalled == 1); + + // By now we should have closed all exchanges and sent all pending acks, so + // there should be no queued-up things in the retransmit table. + NL_TEST_ASSERT(apSuite, rm->TestGetCountRetransTable() == 0); + + engine->Shutdown(); +} + } // namespace app } // namespace chip @@ -562,6 +656,8 @@ const nlTest sTests[] = NL_TEST_DEF("TestWriteRoundtripWithClusterObjects", chip::app::TestWriteInteraction::TestWriteRoundtripWithClusterObjects), NL_TEST_DEF("TestWriteRoundtripWithClusterObjectsVersionMatch", chip::app::TestWriteInteraction::TestWriteRoundtripWithClusterObjectsVersionMatch), NL_TEST_DEF("TestWriteRoundtripWithClusterObjectsVersionMismatch", chip::app::TestWriteInteraction::TestWriteRoundtripWithClusterObjectsVersionMismatch), + NL_TEST_DEF("TestWriteInvalidMessage1", chip::app::TestWriteInteraction::TestWriteInvalidMessage1), + NL_TEST_DEF("TestWriteInvalidMessage2", chip::app::TestWriteInteraction::TestWriteInvalidMessage2), NL_TEST_SENTINEL() }; // clang-format on diff --git a/src/transport/raw/tests/NetworkTestHelpers.h b/src/transport/raw/tests/NetworkTestHelpers.h index bbf3fcb936310e..923653c5f433fd 100644 --- a/src/transport/raw/tests/NetworkTestHelpers.h +++ b/src/transport/raw/tests/NetworkTestHelpers.h @@ -64,7 +64,7 @@ class LoopbackTransportDelegate public: virtual ~LoopbackTransportDelegate() {} - // Called by the loopback transport when it drops a message due to a nonzero mNumMessagesToDrop. + // Called by the loopback transport when it drops a message due to a nonzero mNumMessagesToDrop/mNumMessagesToDropSinceIndex virtual void OnMessageDropped() {} }; @@ -102,19 +102,37 @@ class LoopbackTransport : public Transport::Base { ReturnErrorOnFailure(mMessageSendError); mSentMessageCount++; - - if (mNumMessagesToDrop == 0) + if (mNumMessagesToDropSinceIndex == 0) { - System::PacketBufferHandle receivedMessage = msgBuf.CloneData(); - mPendingMessageQueue.push(PendingMessageItem(address, std::move(receivedMessage))); - mSystemLayer->ScheduleWork(OnMessageReceived, this); + if (mNumMessagesToDrop == 0) + { + System::PacketBufferHandle receivedMessage = msgBuf.CloneData(); + mPendingMessageQueue.push(PendingMessageItem(address, std::move(receivedMessage))); + mSystemLayer->ScheduleWork(OnMessageReceived, this); + } + else + { + mNumMessagesToDrop--; + mDroppedMessageCount++; + if (mDelegate != nullptr) + mDelegate->OnMessageDropped(); + } } else { - mNumMessagesToDrop--; - mDroppedMessageCount++; - if (mDelegate != nullptr) - mDelegate->OnMessageDropped(); + if (mSentMessageCount < mNumMessagesToDropSinceIndex) + { + System::PacketBufferHandle receivedMessage = msgBuf.CloneData(); + mPendingMessageQueue.push(PendingMessageItem(address, std::move(receivedMessage))); + mSystemLayer->ScheduleWork(OnMessageReceived, this); + } + else + { + mNumMessagesToDrop--; + mDroppedMessageCount++; + if (mDelegate != nullptr) + mDelegate->OnMessageDropped(); + } } return CHIP_NO_ERROR; @@ -124,10 +142,11 @@ class LoopbackTransport : public Transport::Base void Reset() { - mNumMessagesToDrop = 0; - mDroppedMessageCount = 0; - mSentMessageCount = 0; - mMessageSendError = CHIP_NO_ERROR; + mNumMessagesToDrop = 0; + mDroppedMessageCount = 0; + mSentMessageCount = 0; + mNumMessagesToDropSinceIndex = 0; + mMessageSendError = CHIP_NO_ERROR; } struct PendingMessageItem @@ -146,6 +165,7 @@ class LoopbackTransport : public Transport::Base uint32_t mNumMessagesToDrop = 0; uint32_t mDroppedMessageCount = 0; uint32_t mSentMessageCount = 0; + uint32_t mNumMessagesToDropSinceIndex = 0; CHIP_ERROR mMessageSendError = CHIP_NO_ERROR; LoopbackTransportDelegate * mDelegate = nullptr; };