blob: 4e616194584037858baed225e234fb8eab32f507 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 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
* This file defines the classes corresponding to CHIP Exchange Context.
*
*/
#pragma once
#include <lib/core/ReferenceCounted.h>
#include <lib/support/BitFlags.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/ReferenceCountedHandle.h>
#include <lib/support/TypeTraits.h>
#include <messaging/ExchangeDelegate.h>
#include <messaging/Flags.h>
#include <messaging/ReliableMessageContext.h>
#include <protocols/Protocols.h>
#include <transport/SessionManager.h>
namespace chip {
namespace Messaging {
class ExchangeManager;
class ExchangeContext;
class ExchangeMessageDispatch;
using ExchangeHandle = ReferenceCountedHandle<ExchangeContext>;
class ExchangeContextDeletor
{
public:
static void Release(ExchangeContext * obj);
};
/**
* @brief
* This class represents an ongoing conversation (ExchangeContext) between two or more nodes.
* It defines methods for encoding and communicating CHIP messages within an ExchangeContext
* over various transport mechanisms, for example, TCP, UDP, or CHIP Reliable Messaging.
*/
class DLL_EXPORT ExchangeContext : public ReliableMessageContext,
public ReferenceCounted<ExchangeContext, ExchangeContextDeletor>,
public SessionReleaseDelegate
{
friend class ExchangeManager;
friend class ExchangeContextDeletor;
public:
typedef System::Clock::Timeout Timeout; // Type used to express the timeout in this ExchangeContext
ExchangeContext(ExchangeManager * em, uint16_t ExchangeId, const SessionHandle & session, bool Initiator,
ExchangeDelegate * delegate);
~ExchangeContext() override;
/**
* Determine whether the context is the initiator of the exchange.
*
* @return Returns 'true' if it is the initiator, else 'false'.
*/
bool IsInitiator() const;
bool IsEncryptionRequired() const { return mDispatch.IsEncryptionRequired(); }
bool IsGroupExchangeContext() const { return mSession && mSession->IsGroupSession(); }
// Implement SessionReleaseDelegate
void OnSessionReleased() override;
/**
* Send a CHIP message on this exchange.
*
* If SendMessage returns success and the message was not expecting a
* response, the exchange will close itself before returning, unless the
* message being sent is a standalone ack. If SendMessage returns failure,
* the caller is responsible for deciding what to do (e.g. closing the
* exchange, trying to re-establish a secure session, etc).
*
* @param[in] protocolId The protocol identifier of the CHIP message to be sent.
*
* @param[in] msgType The message type of the corresponding protocol.
*
* @param[in] msgPayload A handle to the packet buffer holding the CHIP message.
*
* @param[in] sendFlags Flags set by the application for the CHIP message being sent.
*
* @retval #CHIP_ERROR_INVALID_ARGUMENT if an invalid argument was passed to this SendMessage API.
* @retval #CHIP_ERROR_WRONG_MSG_VERSION_FOR_EXCHANGE if there is a mismatch in the specific send operation and the
* CHIP message protocol version that is supported.
* @retval #CHIP_ERROR_NOT_CONNECTED if the context was associated with a connection that is now
* closed.
* @retval #CHIP_ERROR_INCORRECT_STATE if the state of the exchange context is incorrect.
* @retval #CHIP_NO_ERROR if the CHIP layer successfully sent the message down to the
* network layer.
*/
CHIP_ERROR SendMessage(Protocols::Id protocolId, uint8_t msgType, System::PacketBufferHandle && msgPayload,
const SendFlags & sendFlags = SendFlags(SendMessageFlags::kNone));
/**
* A strongly-message-typed version of SendMessage.
*/
template <typename MessageType, typename = std::enable_if_t<std::is_enum<MessageType>::value>>
CHIP_ERROR SendMessage(MessageType msgType, System::PacketBufferHandle && msgPayload,
const SendFlags & sendFlags = SendFlags(SendMessageFlags::kNone))
{
return SendMessage(Protocols::MessageTypeTraits<MessageType>::ProtocolId(), to_underlying(msgType), std::move(msgPayload),
sendFlags);
}
/**
* A notification that we will have SendMessage called on us in the future
* (and should stay open until that happens).
*/
void WillSendMessage() { mFlags.Set(Flags::kFlagWillSendMessage); }
/**
* Handle a received CHIP message on this exchange.
*
* @param[in] messageCounter The message counter of the packet.
* @param[in] payloadHeader A reference to the PayloadHeader object.
* @param[in] peerAddress The address of the sender
* @param[in] msgFlags The message flags corresponding to the received message
* @param[in] msgBuf A handle to the packet buffer holding the CHIP message.
*
* @retval #CHIP_ERROR_INVALID_ARGUMENT if an invalid argument was passed to this HandleMessage API.
* @retval #CHIP_ERROR_INCORRECT_STATE if the state of the exchange context is incorrect.
* @retval #CHIP_NO_ERROR if the CHIP layer successfully delivered the message up to the
* protocol layer.
*/
CHIP_ERROR HandleMessage(uint32_t messageCounter, const PayloadHeader & payloadHeader,
const Transport::PeerAddress & peerAddress, MessageFlags msgFlags,
System::PacketBufferHandle && msgBuf);
ExchangeDelegate * GetDelegate() const { return mDelegate; }
void SetDelegate(ExchangeDelegate * delegate) { mDelegate = delegate; }
ExchangeManager * GetExchangeMgr() const { return mExchangeMgr; }
ReliableMessageContext * GetReliableMessageContext() { return static_cast<ReliableMessageContext *>(this); };
ExchangeMessageDispatch & GetMessageDispatch() { return mDispatch; }
SessionHandle GetSessionHandle() const { return mSession.Get(); }
bool HasSessionHandle() const { return mSession; }
uint16_t GetExchangeId() const { return mExchangeId; }
/*
* In order to use reference counting (see refCount below) we use a hold/free paradigm where users of the exchange
* can hold onto it while it's out of their direct control to make sure it isn't closed before everyone's ready.
* A customized version of reference counting is used since there are some extra stuff to do within Release.
*/
void Close();
void Abort();
void SetResponseTimeout(Timeout timeout);
private:
Timeout mResponseTimeout{ 0 }; // Maximum time to wait for response (in milliseconds); 0 disables response timeout.
ExchangeDelegate * mDelegate = nullptr;
ExchangeManager * mExchangeMgr = nullptr;
ExchangeMessageDispatch & mDispatch;
SessionHolderWithDelegate mSession; // The connection state
uint16_t mExchangeId; // Assigned exchange ID.
/**
* Determine whether a response is currently expected for a message that was sent over
* this exchange. While this is true, attempts to send other messages that expect a response
* will fail.
*
* @return Returns 'true' if response expected, else 'false'.
*/
bool IsResponseExpected() const;
/**
* Determine whether we are expecting our consumer to send a message on
* this exchange (i.e. WillSendMessage was called and the message has not
* yet been sent).
*/
bool IsSendExpected() const { return mFlags.Has(Flags::kFlagWillSendMessage); }
/**
* Track whether we are now expecting a response to a message sent via this exchange (because that
* message had the kExpectResponse flag set in its sendFlags).
*
* @param[in] inResponseExpected A Boolean indicating whether (true) or not
* (false) a response is currently expected on this
* exchange.
*/
void SetResponseExpected(bool inResponseExpected);
/**
* Search for an existing exchange that the message applies to.
*
* @param[in] session The secure session of the received message.
*
* @param[in] packetHeader A reference to the PacketHeader object.
*
* @param[in] payloadHeader A reference to the PayloadHeader object.
*
* @retval true If a match is found.
* @retval false If a match is not found.
*/
bool MatchExchange(const SessionHandle & session, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader);
/**
* Notify our delegate, if any, that we have timed out waiting for a
* response.
*/
void NotifyResponseTimeout();
CHIP_ERROR StartResponseTimer();
void CancelResponseTimer();
static void HandleResponseTimeout(System::Layer * aSystemLayer, void * aAppState);
void DoClose(bool clearRetransTable);
/**
* We have handled an application-level message in some way and should
* re-evaluate out state to see whether we should still be open.
*/
void MessageHandled();
/**
* Updates Sleepy End Device polling mode in the following way:
* - does nothing for exchanges over Bluetooth LE
* - requests fast-polling (active) mode if there are more messages,
* including MRP acknowledgements, expected to be sent or received on
* this exchange.
* - withdraws the request for fast-polling (active) mode, otherwise.
*/
void UpdateSEDPollingMode();
/**
* Requests or withdraws the request for Sleepy End Device fast-polling mode
* based on the argument value.
*
* Note that the device switches to the slow-polling (idle) mode if no
* exchange nor other component requests the fast-polling mode.
*/
void UpdateSEDPollingMode(bool fastPollingMode);
};
} // namespace Messaging
} // namespace chip