From 067afd8dce2126e778cb9b54a689821efda8ac8f Mon Sep 17 00:00:00 2001 From: Nivedita Sarkar Date: Thu, 10 Aug 2023 12:02:01 -0700 Subject: [PATCH] add an async facilitator to not poll output --- .../CHIP/MTROTAProviderDelegateBridge.mm | 110 ++++++++----- .../bdx/AsyncTransferFacilitator.cpp | 153 ++++++++++++++++++ src/protocols/bdx/AsyncTransferFacilitator.h | 138 ++++++++++++++++ src/protocols/bdx/BUILD.gn | 2 + src/protocols/bdx/BdxTransferSession.cpp | 7 +- src/protocols/bdx/BdxTransferSession.h | 22 +++ 6 files changed, 388 insertions(+), 44 deletions(-) create mode 100644 src/protocols/bdx/AsyncTransferFacilitator.cpp create mode 100644 src/protocols/bdx/AsyncTransferFacilitator.h diff --git a/src/darwin/Framework/CHIP/MTROTAProviderDelegateBridge.mm b/src/darwin/Framework/CHIP/MTROTAProviderDelegateBridge.mm index d64522f4ceded7..6989f489476623 100644 --- a/src/darwin/Framework/CHIP/MTROTAProviderDelegateBridge.mm +++ b/src/darwin/Framework/CHIP/MTROTAProviderDelegateBridge.mm @@ -33,7 +33,7 @@ #include #include #include -#include +#include using namespace chip; using namespace chip::app; @@ -58,15 +58,15 @@ constexpr uint32_t kDelayedActionTimeSeconds = 600; constexpr System::Clock::Timeout kBdxTimeout = System::Clock::Seconds16(5 * 60); // OTA Spec mandates >= 5 minutes -constexpr System::Clock::Timeout kBdxPollIntervalMs = System::Clock::Milliseconds32(50); constexpr bdx::TransferRole kBdxRole = bdx::TransferRole::kSender; -class BdxOTASender : public bdx::Responder { +class BdxOTASender : public bdx::AsyncResponder { public: BdxOTASender() {}; CHIP_ERROR PrepareForTransfer(FabricIndex fabricIndex, NodeId nodeId) { + ChipLogError(BDX, "preparefortransfer AsyncResponder"); assertChipStackLockedByCurrentThread(); VerifyOrReturnError(mDelegate != nil, CHIP_ERROR_INCORRECT_STATE); @@ -76,7 +76,8 @@ CHIP_ERROR PrepareForTransfer(FabricIndex fabricIndex, NodeId nodeId) ReturnErrorOnFailure(ConfigureState(fabricIndex, nodeId)); BitFlags flags(bdx::TransferControlFlags::kReceiverDrive); - return Responder::PrepareForTransfer(mSystemLayer, kBdxRole, flags, kMaxBdxBlockSize, kBdxTimeout, kBdxPollIntervalMs); + + return AsyncResponder::PrepareForTransfer(mExchangeMgr, kBdxRole, flags, kMaxBdxBlockSize, kBdxTimeout); } CHIP_ERROR Init(System::Layer * systemLayer, Messaging::ExchangeManager * exchangeMgr) @@ -88,7 +89,7 @@ CHIP_ERROR Init(System::Layer * systemLayer, Messaging::ExchangeManager * exchan VerifyOrReturnError(systemLayer != nullptr, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(exchangeMgr != nullptr, CHIP_ERROR_INCORRECT_STATE); - exchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::BDX::Id, this); + //exchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::BDX::Id, this); mSystemLayer = systemLayer; mExchangeMgr = exchangeMgr; @@ -103,8 +104,8 @@ CHIP_ERROR Shutdown() VerifyOrReturnError(mSystemLayer != nullptr, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mExchangeMgr != nullptr, CHIP_ERROR_INCORRECT_STATE); - mExchangeMgr->UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::BDX::Id); - ResetState(); + //mExchangeMgr->UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::BDX::Id); + ResetState(nullptr); mExchangeMgr = nullptr; mSystemLayer = nullptr; @@ -118,7 +119,7 @@ void ControllerShuttingDown(MTRDeviceController * controller) assertChipStackLockedByCurrentThread(); if (mInitialized && mFabricIndex.Value() == controller.fabricIndex) { - ResetState(); + ResetState(nullptr); } } @@ -128,13 +129,13 @@ void SetDelegate(id delegate, dispatch_queue_t delegateN mDelegate = delegate; mDelegateNotificationQueue = delegateNotificationQueue; } else { - ResetState(); + ResetState(nullptr); mDelegate = nil; mDelegateNotificationQueue = nil; } } - void ResetState() + void ResetState(chip::Messaging::ExchangeContext * ec) { assertChipStackLockedByCurrentThread(); if (mNodeId.HasValue() && mFabricIndex.HasValue()) { @@ -152,16 +153,16 @@ void ResetState() if (!mInitialized) { return; } - Responder::ResetTransfer(); + + AsyncResponder::ResetTransfer(); ++mTransferGeneration; mFabricIndex.ClearValue(); mNodeId.ClearValue(); - if (mExchangeCtx != nullptr) { - mExchangeCtx->Close(); - mExchangeCtx = nullptr; + if (ec != nullptr) { + ec->Close(); + ec = nullptr; } - mInitialized = false; } @@ -172,14 +173,14 @@ void ResetState() static void HandleBdxInitReceivedTimeoutExpired(chip::System::Layer * systemLayer, void * state) { VerifyOrReturn(state != nullptr); - static_cast(state)->ResetState(); + static_cast(state)->ResetState(nullptr); } - CHIP_ERROR OnMessageToSend(TransferSession::OutputEvent & event) + CHIP_ERROR OnMessageToSend(chip::Messaging::ExchangeContext * ec, TransferSession::OutputEvent & event) { assertChipStackLockedByCurrentThread(); - VerifyOrReturnError(mExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(ec != nullptr, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mDelegate != nil, CHIP_ERROR_INCORRECT_STATE); Messaging::SendFlags sendFlags; @@ -191,19 +192,18 @@ CHIP_ERROR OnMessageToSend(TransferSession::OutputEvent & event) } auto & msgTypeData = event.msgTypeData; + ChipLogError(BDX, "OnMessageToSend msgTypeData.MessageType = %hu", msgTypeData.MessageType); // If there's an error sending the message, close the exchange and call ResetState. // TODO: If we can remove the !mInitialized check in ResetState(), just calling ResetState() will suffice here. CHIP_ERROR err - = mExchangeCtx->SendMessage(msgTypeData.ProtocolId, msgTypeData.MessageType, std::move(event.MsgData), sendFlags); + = ec->SendMessage(msgTypeData.ProtocolId, msgTypeData.MessageType, std::move(event.MsgData), sendFlags); if (err != CHIP_NO_ERROR) { - mExchangeCtx->Close(); - mExchangeCtx = nullptr; - ResetState(); + ResetState(ec); } else if (event.msgTypeData.HasMessageType(Protocols::SecureChannel::MsgType::StatusReport)) { // If the send was successful for a status report, since we are not expecting a response the exchange context is // already closed. We need to null out the reference to avoid having a dangling pointer. - mExchangeCtx = nullptr; - ResetState(); + ec = nullptr; + ResetState(ec); } return err; } @@ -219,7 +219,7 @@ CHIP_ERROR OnMessageToSend(TransferSession::OutputEvent & event) return bdx::StatusCode::kUnknown; } - CHIP_ERROR OnTransferSessionBegin(TransferSession::OutputEvent & event) + CHIP_ERROR OnTransferSessionBegin(chip::Messaging::ExchangeContext * ec, TransferSession::OutputEvent & event) { assertChipStackLockedByCurrentThread(); // Once we receive the BDX init, cancel the BDX Init timeout and start the BDX session @@ -231,6 +231,7 @@ CHIP_ERROR OnTransferSessionBegin(TransferSession::OutputEvent & event) VerifyOrReturnError(mNodeId.HasValue(), CHIP_ERROR_INCORRECT_STATE); uint16_t fdl = 0; auto fd = mTransfer.GetFileDesignator(fdl); + ChipLogError(BDX, "OnTransferSessionBegin fd %s", fd); VerifyOrReturnError(fdl <= bdx::kMaxFileDesignatorLen, CHIP_ERROR_INVALID_ARGUMENT); CharSpan fileDesignatorSpan(Uint8::to_const_char(fd), fdl); @@ -242,7 +243,9 @@ CHIP_ERROR OnTransferSessionBegin(TransferSession::OutputEvent & event) auto offset = @(mTransfer.GetStartOffset()); auto * controller = [[MTRDeviceControllerFactory sharedInstance] runningControllerForFabricIndex:mFabricIndex.Value()]; + ChipLogError(BDX, "OnTransferSessionBegin runningControllerForFabricIndex"); VerifyOrReturnError(controller != nil, CHIP_ERROR_INCORRECT_STATE); + ChipLogError(BDX, "OnTransferSessionBegin runningControllerForFabricIndex1"); auto transferGeneration = mTransferGeneration; @@ -253,13 +256,14 @@ CHIP_ERROR OnTransferSessionBegin(TransferSession::OutputEvent & event) if (!mInitialized || mTransferGeneration != transferGeneration) { // Callback for a stale transfer. + AsyncResponder::OnEventHandled(ec, CHIP_ERROR_INCORRECT_STATE); return; } if (error != nil) { CHIP_ERROR err = [MTRError errorToCHIPErrorCode:error]; LogErrorOnFailure(err); - LogErrorOnFailure(mTransfer.AbortTransfer(GetBdxStatusCodeFromChipError(err))); + AsyncResponder::OnEventHandled(ec, err); return; } @@ -273,6 +277,7 @@ CHIP_ERROR OnTransferSessionBegin(TransferSession::OutputEvent & event) acceptData.Length = mTransfer.GetTransferLength(); LogErrorOnFailure(mTransfer.AcceptTransfer(acceptData)); + AsyncResponder::OnEventHandled(ec, [MTRError errorToCHIPErrorCode:error]); } errorHandler:^(NSError *) { // Not much we can do here @@ -285,6 +290,7 @@ CHIP_ERROR OnTransferSessionBegin(TransferSession::OutputEvent & event) dispatch_async(mDelegateNotificationQueue, ^{ if ([strongDelegate respondsToSelector:@selector (handleBDXTransferSessionBeginForNodeID:controller:fileDesignator:offset:completion:)]) { + ChipLogError(BDX, "handleBDXTransferSessionBeginForNodeID called"); [strongDelegate handleBDXTransferSessionBeginForNodeID:nodeId controller:controller fileDesignator:fileDesignator @@ -302,7 +308,7 @@ CHIP_ERROR OnTransferSessionBegin(TransferSession::OutputEvent & event) return CHIP_NO_ERROR; } - CHIP_ERROR OnTransferSessionEnd(TransferSession::OutputEvent & event) + CHIP_ERROR OnTransferSessionEnd(chip::Messaging::ExchangeContext * ec, TransferSession::OutputEvent & event) { assertChipStackLockedByCurrentThread(); @@ -328,12 +334,12 @@ CHIP_ERROR OnTransferSessionEnd(TransferSession::OutputEvent & event) error:[MTRError errorForCHIPErrorCode:error]]; }); } - - ResetState(); + AsyncResponder::OnEventHandled(ec, error); + ResetState(ec); return CHIP_NO_ERROR; } - CHIP_ERROR OnBlockQuery(TransferSession::OutputEvent & event) + CHIP_ERROR OnBlockQuery(chip::Messaging::ExchangeContext * ec, TransferSession::OutputEvent & event) { assertChipStackLockedByCurrentThread(); @@ -360,11 +366,12 @@ CHIP_ERROR OnBlockQuery(TransferSession::OutputEvent & event) if (!mInitialized || mTransferGeneration != transferGeneration) { // Callback for a stale transfer. + AsyncResponder::OnEventHandled(ec, CHIP_ERROR_INCORRECT_STATE); return; } if (data == nil) { - LogErrorOnFailure(mTransfer.AbortTransfer(bdx::StatusCode::kUnknown)); + AsyncResponder::OnEventHandled(ec, CHIP_ERROR_INCORRECT_STATE); return; } @@ -376,8 +383,8 @@ CHIP_ERROR OnBlockQuery(TransferSession::OutputEvent & event) CHIP_ERROR err = mTransfer.PrepareBlock(blockData); if (CHIP_NO_ERROR != err) { LogErrorOnFailure(err); - LogErrorOnFailure(mTransfer.AbortTransfer(bdx::StatusCode::kUnknown)); } + AsyncResponder::OnEventHandled(ec, err); } errorHandler:^(NSError *) { // Not much we can do here @@ -411,16 +418,19 @@ CHIP_ERROR OnBlockQuery(TransferSession::OutputEvent & event) return CHIP_NO_ERROR; } - void HandleTransferSessionOutput(TransferSession::OutputEvent & event) override + void HandleAsyncTransferSessionOutput(chip::Messaging::ExchangeContext * ec, TransferSession::OutputEvent & event) override { + ChipLogError(BDX, "HandleAsyncTransferSessionOutput %s", event.ToString(event.EventType)); VerifyOrReturn(mDelegate != nil); - + ChipLogError(BDX, "HandleTransferSessionOutput event %s", event.ToString(event.EventType)); CHIP_ERROR err = CHIP_NO_ERROR; switch (event.EventType) { case TransferSession::OutputEventType::kInitReceived: - err = OnTransferSessionBegin(event); + err = OnTransferSessionBegin(ec, event); if (err != CHIP_NO_ERROR) { - LogErrorOnFailure(mTransfer.AbortTransfer(GetBdxStatusCodeFromChipError(err))); + LogErrorOnFailure(err); + AsyncResponder::OnEventHandled(ec, err); + ResetState(ec); } break; case TransferSession::OutputEventType::kStatusReceived: @@ -429,14 +439,29 @@ void HandleTransferSessionOutput(TransferSession::OutputEvent & event) override case TransferSession::OutputEventType::kAckEOFReceived: case TransferSession::OutputEventType::kInternalError: case TransferSession::OutputEventType::kTransferTimeout: - err = OnTransferSessionEnd(event); + err = OnTransferSessionEnd(ec, event); + if (err != CHIP_NO_ERROR) { + LogErrorOnFailure(err); + AsyncResponder::OnEventHandled(ec, err); + ResetState(ec); + } break; case TransferSession::OutputEventType::kQueryWithSkipReceived: case TransferSession::OutputEventType::kQueryReceived: - err = OnBlockQuery(event); + err = OnBlockQuery(ec, event); + if (err != CHIP_NO_ERROR) { + LogErrorOnFailure(err); + AsyncResponder::OnEventHandled(ec, err); + ResetState(ec); + } break; case TransferSession::OutputEventType::kMsgToSend: - err = OnMessageToSend(event); + err = OnMessageToSend(ec, event); + if (err != CHIP_NO_ERROR) { + LogErrorOnFailure(err); + AsyncResponder::OnEventHandled(ec, err); + ResetState(ec); + } break; case TransferSession::OutputEventType::kNone: case TransferSession::OutputEventType::kAckReceived: @@ -461,7 +486,7 @@ CHIP_ERROR ConfigureState(chip::FabricIndex fabricIndex, chip::NodeId nodeId) VerifyOrReturnError(mFabricIndex.Value() == fabricIndex && mNodeId.Value() == nodeId, CHIP_ERROR_BUSY); // Reset stale connection from the same Node if exists. - ResetState(); + ResetState(nullptr); } // Start a timer to track whether we receive a BDX init after a successful query image in a reasonable amount of time @@ -700,7 +725,7 @@ bool GetPeerNodeInfo(CommandHandler * commandHandler, const ConcreteCommandPath handle.Release(); // We need to reset state here to clean up any initialization we might have done including starting the BDX // timeout timer while preparing for transfer if any failure occurs afterwards. - gOtaSender.ResetState(); + gOtaSender.ResetState(nullptr); return; } @@ -711,12 +736,13 @@ bool GetPeerNodeInfo(CommandHandler * commandHandler, const ConcreteCommandPath LogErrorOnFailure(err); handler->AddStatus(cachedCommandPath, StatusIB(err).mStatus); handle.Release(); - gOtaSender.ResetState(); + gOtaSender.ResetState(nullptr); return; } delegateResponse.imageURI.SetValue(uri); handler->AddResponse(cachedCommandPath, delegateResponse); handle.Release(); + gOtaSender.OnEventHandled(commandObj->GetExchangeContext(), CHIP_NO_ERROR); } errorHandler:^(NSError *) { // Not much we can do here diff --git a/src/protocols/bdx/AsyncTransferFacilitator.cpp b/src/protocols/bdx/AsyncTransferFacilitator.cpp new file mode 100644 index 00000000000000..b9df15bbf4de4c --- /dev/null +++ b/src/protocols/bdx/AsyncTransferFacilitator.cpp @@ -0,0 +1,153 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AsyncTransferFacilitator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { + +namespace bdx { + +CHIP_ERROR AsyncTransferFacilitator::OnMessageReceived(chip::Messaging::ExchangeContext * ec, const chip::PayloadHeader & payloadHeader, + chip::System::PacketBufferHandle && payload) +{ + // Get the next output event + TransferSession::OutputEvent outEvent; + + ChipLogDetail(BDX, "OnMessageReceived %s: message " ChipLogFormatMessageType " protocol " ChipLogFormatProtocolId, __FUNCTION__, + payloadHeader.GetMessageType(), ChipLogValueProtocolId(payloadHeader.GetProtocolID())); + CHIP_ERROR err = + mTransfer.HandleMessageReceived(payloadHeader, std::move(payload), System::SystemClock().GetMonotonicTimestamp()); + if (err != CHIP_NO_ERROR) + { + ChipLogError(BDX, "failed to handle message: %" CHIP_ERROR_FORMAT, err.Format()); + } + + // Almost every BDX message will follow up with a response on the exchange. Even messages that might signify the end of a + // transfer could necessitate a response if they are received at the wrong time. + // For this reason, it is left up to the application logic to call ExchangeContext::Close() when it has determined that the + // transfer is finished. + ec->WillSendMessage(); + + do + { + mTransfer.GetNextAction(outEvent); + + ChipLogError(BDX, "AsyncTransferFacilitator outEvent %s", outEvent.ToString(outEvent.EventType)); + + if (outEvent.EventType == TransferSession::OutputEventType::kTransferTimeout) { + OnResponseTimeout(ec); + } else { + HandleAsyncTransferSessionOutput(ec, outEvent); + } + ChipLogError(BDX, "AsyncTransferFacilitator register"); + //mExchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::BDX::Id, this); + } while (outEvent.EventType != TransferSession::OutputEventType::kNone); + + + return err; +} + +void AsyncTransferFacilitator::OnResponseTimeout(Messaging::ExchangeContext * ec) +{ + ChipLogError(BDX, "%s, ec: " ChipLogFormatExchange, __FUNCTION__, ChipLogValueExchange(ec)); + mTransfer.Reset(); +} + +CHIP_ERROR AsyncResponder::PrepareForTransfer(chip::Messaging::ExchangeManager * exchangeManager, TransferRole role, BitFlags xferControlOpts, + uint16_t maxBlockSize, System::Clock::Timeout timeout) +{ + mExchangeMgr = exchangeManager; + ReturnErrorOnFailure(mTransfer.WaitForTransfer(role, xferControlOpts, maxBlockSize, timeout)); + + + if (role == TransferRole::kSender) + { + mExchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::BDX::Id, this); + //mExchangeMgr->RegisterUnsolicitedMessageHandlerForProtocol(Protocols::SecureChannel::Id, this); + //mExchangeMgr->RegisterUnsolicitedMessageHandlerForType(Protocols::BDX::Id, static_cast(TransferSession::OutputEventType::kMsgToSend),this); + } + + // create a manager for nodeid + + // register for init received on t + + return CHIP_NO_ERROR; +} + +void AsyncResponder::ResetTransfer() +{ + mExchangeMgr->UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::BDX::Id); + mExchangeMgr = nullptr; + mTransfer.Reset(); +} + +bdx::StatusCode AsyncResponder::GetBdxStatusCodeFromChipError(CHIP_ERROR err) + { + if (err == CHIP_ERROR_INCORRECT_STATE) { + return bdx::StatusCode::kUnexpectedMessage; + } + if (err == CHIP_ERROR_INVALID_ARGUMENT) { + return bdx::StatusCode::kBadMessageContents; + } + return bdx::StatusCode::kUnknown; + } + +void AsyncResponder::OnEventHandled(chip::Messaging::ExchangeContext * ec, CHIP_ERROR error) +{ + ChipLogError(BDX, "AsyncTransferFacilitator error %d", error.AsInteger()); + if (error != CHIP_NO_ERROR) + { + mTransfer.AbortTransfer(GetBdxStatusCodeFromChipError(error)); + } + + // Get the next output event + TransferSession::OutputEvent outEvent; + do + { + mTransfer.GetNextAction(outEvent); + + ChipLogError(BDX, "AsyncTransferFacilitator OnEventHandled outEvent %s", outEvent.ToString(outEvent.EventType)); + + if (outEvent.EventType == TransferSession::OutputEventType::kTransferTimeout) { + OnResponseTimeout(ec); + } else { + HandleAsyncTransferSessionOutput(ec, outEvent); + } + } while (outEvent.EventType != TransferSession::OutputEventType::kNone); +} + +CHIP_ERROR Initiator::InitiateTransfer(TransferRole role, const TransferSession::TransferInitData & initData, + System::Clock::Timeout timeout) +{ + ReturnErrorOnFailure(mTransfer.StartTransfer(role, initData, timeout)); + + + return CHIP_NO_ERROR; +} + +} // namespace bdx +} // namespace chip \ No newline at end of file diff --git a/src/protocols/bdx/AsyncTransferFacilitator.h b/src/protocols/bdx/AsyncTransferFacilitator.h new file mode 100644 index 00000000000000..fcd398b8ae9aad --- /dev/null +++ b/src/protocols/bdx/AsyncTransferFacilitator.h @@ -0,0 +1,138 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file BdxEndpoint.h + * + * This file defines interfaces for connecting the BDX state machine (TransferSession) to the messaging layer. + */ + +#include +#include +#include +#include +#include +#include +#include + +#pragma once + +namespace chip { +namespace bdx { + +/** + * An abstract class with methods for handling BDX messages from an ExchangeContext and getting the next action from a TransferSession + * state machine based on the state of the session and the message received or sent. + * + * This class does not define any methods for beginning a transfer or initializing the underlying TransferSession object (see + * Initiator and Responder below). + * A CHIP node may have many AsyncTransferFacilitator instances but only one AsyncTransferFacilitator should be used for each BDX transfer. + */ +class AsyncTransferFacilitator : public Messaging::ExchangeDelegate, public Messaging::UnsolicitedMessageHandler +{ +public: + AsyncTransferFacilitator() : mExchangeMgr(nullptr), mSystemLayer(nullptr) {} + ~AsyncTransferFacilitator() override = default; + void OnResponseTimeout(Messaging::ExchangeContext * ec) override; + + /** + * This method should be implemented to contain business-logic handling of BDX messages and other TransferSession events. + * + * NOTE: It is the responsiblity of the implementer to Close the underlying ExchangeContext when it has determined that the + * transfer is finished. This class assumes that a response message will be sent for all received messages. + * + * @param[in] event An OutputEvent that contains output from the TransferSession object. + */ + virtual void HandleAsyncTransferSessionOutput(chip::Messaging::ExchangeContext * ec, TransferSession::OutputEvent & event) = 0; + +private: + //// UnsolicitedMessageHandler Implementation //// + CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate) override + { + // TODO: Implement a bdx manager, which dispatch bdx messages to bdx transections. + // directly. + newDelegate = this; + return CHIP_NO_ERROR; + } + + // Inherited from ExchangeContext + CHIP_ERROR OnMessageReceived(chip::Messaging::ExchangeContext * ec, const chip::PayloadHeader & payloadHeader, + chip::System::PacketBufferHandle && payload) override; + +protected: + + TransferSession mTransfer; + Messaging::ExchangeManager * mExchangeMgr; + System::Layer * mSystemLayer; +}; + +/** + * A AsyncTransferFacilitator that is initialized to respond to an incoming BDX transfer request. + * + * Provides a method for initializing the TransferSession member but still needs to be extended to implement + * HandleAsyncTransferSessionOutput. It is intended that this class will be used as a delegate for handling an unsolicited BDX message. + */ +class AsyncResponder : public AsyncTransferFacilitator +{ +public: + /** + * Initialize the TransferSession state machine to be ready for an incoming transfer request + * + * @param[in] role The role of the Responder: Sender or Receiver of BDX data + * @param[in] xferControlOpts Supported transfer modes (see TransferControlFlags) + * @param[in] maxBlockSize The supported maximum size of BDX Block data + * @param[in] timeout The chosen timeout delay for the BDX transfer + */ + CHIP_ERROR PrepareForTransfer(Messaging::ExchangeManager * exchangeManager, TransferRole role, BitFlags xferControlOpts, + uint16_t maxBlockSize, System::Clock::Timeout timeout); + + void ResetTransfer(); + + /** + * Notifies the transfer facilitator that an output event has been handled by the delegate + * + * @param[in] ec The exchange context of the Transfer facilitator that the delegate is using + * @param[in] error The CHIP_ERROR if there were any errors in handling the event, otherwise CHIP_NO_ERROR is passed + */ + void OnEventHandled(chip::Messaging::ExchangeContext * ec, CHIP_ERROR error); + +private: + bdx::StatusCode GetBdxStatusCodeFromChipError(CHIP_ERROR err); +}; + +/** + * A AsyncTransferFacilitator that initiates a BDX transfer. + * + * Provides a method for initializing the TransferSession member (thus beginning the transfer) but still needs to be extended to + * implement HandleAsyncTransferSessionOutput. + */ +class Initiator : public AsyncTransferFacilitator +{ +public: + /** + * Initialize the TransferSession state machine to prepare a transfer request message (does not send the message) + * + * @param[in] role The role of the Initiator: Sender or Receiver of BDX data + * @param[in] initData Data needed for preparing a transfer request BDX message + * @param[in] timeout The chosen timeout delay for the BDX transfer in milliseconds + */ + CHIP_ERROR InitiateTransfer(TransferRole role, const TransferSession::TransferInitData & initData, + System::Clock::Timeout timeout); +}; + +} // namespace bdx +} // namespace chip \ No newline at end of file diff --git a/src/protocols/bdx/BUILD.gn b/src/protocols/bdx/BUILD.gn index c9cfe91ac45a39..6660cf0f74ef34 100644 --- a/src/protocols/bdx/BUILD.gn +++ b/src/protocols/bdx/BUILD.gn @@ -26,6 +26,8 @@ static_library("bdx") { "BdxUri.h", "TransferFacilitator.cpp", "TransferFacilitator.h", + "AsyncTransferFacilitator.cpp", + "AsyncTransferFacilitator.h", ] cflags = [ "-Wconversion" ] diff --git a/src/protocols/bdx/BdxTransferSession.cpp b/src/protocols/bdx/BdxTransferSession.cpp index f6c54985ab5cd1..5d5c32fb3524a5 100644 --- a/src/protocols/bdx/BdxTransferSession.cpp +++ b/src/protocols/bdx/BdxTransferSession.cpp @@ -64,10 +64,13 @@ TransferSession::TransferSession() mSuppportedXferOpts.ClearAll(); } -void TransferSession::PollOutput(OutputEvent & event, System::Clock::Timestamp curTime) +void TransferSession::GetNextAction(OutputEvent & event) { - event = OutputEvent(OutputEventType::kNone); + PollOutput(event, System::SystemClock().GetMonotonicTimestamp()); +} +void TransferSession::PollOutput(OutputEvent & event, System::Clock::Timestamp curTime) +{ if (mShouldInitTimeoutStart) { mTimeoutStartTime = curTime; diff --git a/src/protocols/bdx/BdxTransferSession.h b/src/protocols/bdx/BdxTransferSession.h index f5ef397ca7d9e8..757b65a612928d 100644 --- a/src/protocols/bdx/BdxTransferSession.h +++ b/src/protocols/bdx/BdxTransferSession.h @@ -144,6 +144,28 @@ class DLL_EXPORT TransferSession static OutputEvent QueryWithSkipEvent(TransferSkipData bytesToSkip); }; + /** + * @brief + * Gets the next pending output from the transfer session along with any data for the caller to take action on. + * + * It is a wrapper around PollOutput which is a misnomer since the intent of the PollOutput was not to use a polling + * mechanism to get the next action for the client to take. It is highly encourgaed to use GetNextAction in lieu of + * PollOuput to get the pending output event. + * + * This method should be called asynchronously based on events received by the exchange context or events sent by + * the entity using the Transfer session for BDX. + * + * It is possible that consecutive calls to this method may emit different outputs depending on the state of the + * TransferSession object and so we need to call this until we get an event of type - OutputEventType::kNone + * + * Note that if the type outputted is kMsgToSend, the caller is expected to send the message immediately + * + * See OutputEventType for all possible output event types. + * + * @param event Reference to an OutputEvent struct that will be filled out with any pending output data + */ + void GetNextAction(OutputEvent & event); + /** * @brief * Indicates the presence of pending output and includes any data for the caller to take action on.