blob: c6867474804d7c2cfb32754e599785b7b557ff6b [file] [log] [blame]
/*
*
* Copyright (c) 2020 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 implements the ExchangeContext class.
*
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
#include <core/CHIPCore.h>
#include <core/CHIPEncoding.h>
#include <messaging/ExchangeContext.h>
#include <messaging/ExchangeMgr.h>
#include <protocols/Protocols.h>
#include <support/logging/CHIPLogging.h>
#include <system/SystemTimer.h>
using namespace chip::Encoding;
using namespace chip::Inet;
using namespace chip::System;
namespace chip {
static void DefaultOnMessageReceived(ExchangeContext * ec, const PacketHeader & packetHeader, uint32_t protocolId, uint8_t msgType,
PacketBufferHandle payload)
{
ChipLogError(ExchangeManager, "Dropping unexpected message %08" PRIX32 ":%d %04" PRIX16 " MsgId:%08" PRIX32, protocolId,
msgType, ec->GetExchangeId(), packetHeader.GetMessageId());
}
bool ExchangeContext::IsInitiator() const
{
return mFlags.Has(ExFlagValues::kFlagInitiator);
}
bool ExchangeContext::IsResponseExpected() const
{
return mFlags.Has(ExFlagValues::kFlagResponseExpected);
}
void ExchangeContext::SetResponseExpected(bool inResponseExpected)
{
mFlags.Set(ExFlagValues::kFlagResponseExpected, inResponseExpected);
}
CHIP_ERROR ExchangeContext::SendMessage(uint16_t protocolId, uint8_t msgType, PacketBuffer * msgBuf, uint16_t sendFlags,
void * msgCtxt)
{
CHIP_ERROR err = CHIP_NO_ERROR;
PayloadHeader payloadHeader;
// Don't let method get called on a freed object.
VerifyOrDie(mExchangeMgr != nullptr && GetReferenceCount() > 0);
// we hold the exchange context here in case the entity that
// originally generated it tries to close it as a result of
// an error arising below. at the end, we have to close it.
Retain();
// Set the exchange ID for this header.
payloadHeader.SetExchangeID(mExchangeId);
// Set the protocol ID for this header.
payloadHeader.SetProtocolID(protocolId);
// Set the message type for this header.
payloadHeader.SetMessageType(msgType);
// If a response message is expected...
if ((sendFlags & kSendFlag_ExpectResponse) != 0)
{
// Only one 'response expected' message can be outstanding at a time.
VerifyOrExit(!IsResponseExpected(), err = CHIP_ERROR_INCORRECT_STATE);
SetResponseExpected(true);
// Arm the response timer if a timeout has been specified.
if (mResponseTimeout > 0)
{
err = StartResponseTimer();
SuccessOrExit(err);
}
}
payloadHeader.SetInitiator(IsInitiator());
err = mExchangeMgr->GetSessionMgr()->SendMessage(payloadHeader, mPeerNodeId, msgBuf);
msgBuf = nullptr;
SuccessOrExit(err);
exit:
if (err != CHIP_NO_ERROR && IsResponseExpected())
{
CancelResponseTimer();
SetResponseExpected(false);
}
if (msgBuf != nullptr && (sendFlags & kSendFlag_RetainBuffer) == 0)
{
PacketBuffer::Free(msgBuf);
}
// Release the reference to the exchange context acquired above. Under normal circumstances
// this will merely decrement the reference count, without actually freeing the exchange context.
// However if one of the function calls in this method resulted in a callback to the protocol,
// the protocol may have released its reference, resulting in the exchange context actually
// being freed here.
Release();
return err;
}
void ExchangeContext::DoClose(bool clearRetransTable)
{
// Clear protocol callbacks
if (mDelegate != nullptr)
{
mDelegate->OnExchangeClosing(this);
}
mDelegate = nullptr;
// Cancel the response timer.
CancelResponseTimer();
}
/**
* Gracefully close an exchange context. This call decrements the
* reference count and releases the exchange when the reference
* count goes to zero.
*
*/
void ExchangeContext::Close()
{
VerifyOrDie(mExchangeMgr != nullptr && GetReferenceCount() > 0);
#if defined(CHIP_EXCHANGE_CONTEXT_DETAIL_LOGGING)
ChipLogProgress(ExchangeManager, "ec id: %d [%04" PRIX16 "], %s", (this - mExchangeMgr->ContextPool + 1), mExchangeId,
__func__);
#endif
DoClose(false);
Release();
}
/**
* Abort the Exchange context immediately and release all
* references to it.
*
*/
void ExchangeContext::Abort()
{
VerifyOrDie(mExchangeMgr != nullptr && GetReferenceCount() > 0);
#if defined(CHIP_EXCHANGE_CONTEXT_DETAIL_LOGGING)
ChipLogProgress(ExchangeManager, "ec id: %d [%04" PRIX16 "], %s", (this - mExchangeMgr->ContextPool + 1), mExchangeId,
__func__);
#endif
DoClose(true);
Release();
}
void ExchangeContext::Reset()
{
*this = ExchangeContext();
}
ExchangeContext * ExchangeContext::Alloc(ExchangeManager * em, uint16_t ExchangeId, uint64_t PeerNodeId, bool Initiator,
ExchangeDelegate * delegate)
{
VerifyOrDie(delegate != nullptr);
VerifyOrDie(mExchangeMgr == nullptr && GetReferenceCount() == 0);
Reset();
Retain();
mExchangeMgr = em;
em->IncrementContextsInUse();
mExchangeId = ExchangeId;
mPeerNodeId = PeerNodeId;
mFlags.Set(ExFlagValues::kFlagInitiator, Initiator);
mDelegate = delegate;
#if defined(CHIP_EXCHANGE_CONTEXT_DETAIL_LOGGING)
ChipLogProgress(ExchangeManager, "ec++ id: %d, inUse: %d, addr: 0x%x", (this - em->ContextPool + 1), em->GetContextsInUse(),
this);
#endif
SYSTEM_STATS_INCREMENT(chip::System::Stats::kExchangeMgr_NumContexts);
return this;
}
void ExchangeContext::Free()
{
VerifyOrDie(mExchangeMgr != nullptr && GetReferenceCount() == 0);
// Ideally, in this scenario, the retransmit table should
// be clear of any outstanding messages for this context and
// the boolean parameter passed to DoClose() should not matter.
ExchangeManager * em = mExchangeMgr;
DoClose(false);
mExchangeMgr = nullptr;
em->DecrementContextsInUse();
#if defined(CHIP_EXCHANGE_CONTEXT_DETAIL_LOGGING)
ChipLogProgress(ExchangeManager, "ec-- id: %d [%04" PRIX16 "], inUse: %d, addr: 0x%x", (this - em->ContextPool + 1),
mExchangeId, em->GetContextsInUse(), this);
#endif
SYSTEM_STATS_DECREMENT(chip::System::Stats::kExchangeMgr_NumContexts);
}
bool ExchangeContext::MatchExchange(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader)
{
// A given message is part of a particular exchange if...
return
// The exchange identifier of the message matches the exchange identifier of the context.
(mExchangeId == payloadHeader.GetExchangeID())
// AND The message was received from the peer node associated with the exchange, or the peer node identifier is 'any'.
&& ((mPeerNodeId == kAnyNodeId) || (mPeerNodeId == packetHeader.GetSourceNodeId().Value()))
// AND The message was sent by an initiator and the exchange context is a responder (IsInitiator==false)
// OR The message was sent by a responder and the exchange context is an initiator (IsInitiator==true) (for the broadcast
// case, the initiator is ill defined)
&& (payloadHeader.IsInitiator() != IsInitiator());
}
CHIP_ERROR ExchangeContext::StartResponseTimer()
{
System::Layer * lSystemLayer = mExchangeMgr->GetSessionMgr()->SystemLayer();
if (lSystemLayer == nullptr)
{
// this is an assertion error, which shall never happen
return CHIP_ERROR_INTERNAL;
}
return lSystemLayer->StartTimer(mResponseTimeout, HandleResponseTimeout, this);
}
void ExchangeContext::CancelResponseTimer()
{
System::Layer * lSystemLayer = mExchangeMgr->GetSessionMgr()->SystemLayer();
if (lSystemLayer == nullptr)
{
// this is an assertion error, which shall never happen
return;
}
lSystemLayer->CancelTimer(HandleResponseTimeout, this);
}
void ExchangeContext::HandleResponseTimeout(System::Layer * aSystemLayer, void * aAppState, System::Error aError)
{
ExchangeContext * ec = reinterpret_cast<ExchangeContext *>(aAppState);
if (ec == nullptr)
return;
// NOTE: we don't set mResponseExpected to false here because the response could still arrive. If the user
// wants to never receive the response, they must close the exchange context.
ExchangeDelegate * delegate = ec->GetDelegate();
// Call the user's timeout handler.
if (delegate != nullptr)
delegate->OnResponseTimeout(ec);
}
CHIP_ERROR ExchangeContext::HandleMessage(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader,
PacketBufferHandle msgBuf)
{
CHIP_ERROR err = CHIP_NO_ERROR;
uint16_t protocolId = 0;
uint8_t messageType = 0;
// We hold a reference to the ExchangeContext here to
// guard against Close() calls(decrementing the reference
// count) by the protocol before the CHIP Exchange
// layer has completed its work on the ExchangeContext.
Retain();
protocolId = payloadHeader.GetProtocolID();
messageType = payloadHeader.GetMessageType();
// Since we got the response, cancel the response timer.
CancelResponseTimer();
// If the context was expecting a response to a previously sent message, this message
// is implicitly that response.
SetResponseExpected(false);
if (mDelegate != nullptr)
{
mDelegate->OnMessageReceived(this, packetHeader, protocolId, messageType, std::move(msgBuf));
}
else
{
DefaultOnMessageReceived(this, packetHeader, protocolId, messageType, std::move(msgBuf));
}
// Release the reference to the ExchangeContext that was held at the beginning of this function.
// This call should also do the needful of closing the ExchangeContext if the protocol has
// already made a prior call to Close().
Release();
return err;
}
} // namespace chip