text: /*
   *    Copyright (c) 2020-2021 Project CHIP Authors
   *    All rights reserved.
   *
   *    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 CHIP reliable message protocol.
   *
   */
  
  #include <inttypes.h>
  
  #include <messaging/ExchangeContext.h>
  #include <messaging/ExchangeMgr.h>
  #include <messaging/ReliableMessageContext.h>
  
  #include <lib/core/CHIPEncoding.h>
  #include <lib/support/CodeUtils.h>
  #include <lib/support/Defer.h>
  #include <messaging/ErrorCategory.h>
  #include <messaging/Flags.h>
  #include <messaging/ReliableMessageMgr.h>
  #include <protocols/Protocols.h>
  #include <protocols/secure_channel/Constants.h>
  
  namespace chip {
  namespace Messaging {
  
  ReliableMessageContext::ReliableMessageContext() : mNextAckTime(0), mPendingPeerAckMessageCounter(0) {}
  
  ExchangeContext * ReliableMessageContext::GetExchangeContext()
  {
      return static_cast<ExchangeContext *>(this);
  }
  
  ReliableMessageMgr * ReliableMessageContext::GetReliableMessageMgr()
  {
      return static_cast<ExchangeContext *>(this)->GetExchangeMgr()->GetReliableMessageMgr();
  }
  
  CHIP_ERROR ReliableMessageContext::FlushAcks()
  {
      CHIP_ERROR err = CHIP_NO_ERROR;
  
      if (IsAckPending())
      {
          err = SendStandaloneAckMessage();
  
          if (err == CHIP_NO_ERROR)
          {
              ChipLogDetail(ExchangeManager,
                            "Flushed pending ack for MessageCounter:" ChipLogFormatMessageCounter
                            " on exchange " ChipLogFormatExchange,
                            mPendingPeerAckMessageCounter, ChipLogValueExchange(GetExchangeContext()));
          }
      }
  
      return err;
  }
  
  /**
   *  Process received Ack. Remove the corresponding message context from the RetransTable and execute the application
   *  callback
   *
   *  @note
   *    This message is part of the CHIP Reliable Messaging protocol.
   *
   *  @param[in]    ackMessageCounter         The acknowledged message counter of the incoming message.
   */
  void ReliableMessageContext::HandleRcvdAck(uint32_t ackMessageCounter)
  {
      // Msg is an Ack; Check Retrans Table and remove message context
      if (!GetReliableMessageMgr()->CheckAndRemRetransTable(this, ackMessageCounter))
      {
          // This can happen quite easily due to a packet with a piggyback ack
          // being lost and retransmitted.
          ChipLogDetail(ExchangeManager,
                        "CHIP MessageCounter:" ChipLogFormatMessageCounter " not in RetransTable on exchange " ChipLogFormatExchange,
                        ackMessageCounter, ChipLogValueExchange(GetExchangeContext()));
      }
      else
      {
          ChipLogDetail(ExchangeManager,
                        "Removed CHIP MessageCounter:" ChipLogFormatMessageCounter
                        " from RetransTable on exchange " ChipLogFormatExchange,
                        ackMessageCounter, ChipLogValueExchange(GetExchangeContext()));
      }
  }
  
  CHIP_ERROR ReliableMessageContext::HandleNeedsAck(uint32_t messageCounter, BitFlags<MessageFlagValues> messageFlags)
  
  {
      CHIP_ERROR err = HandleNeedsAckInner(messageCounter, messageFlags);
  
      // Schedule next physical wakeup on function exit
      GetReliableMessageMgr()->StartTimer();
  
      return err;
  }
  
  CHIP_ERROR ReliableMessageContext::HandleNeedsAckInner(uint32_t messageCounter, BitFlags<MessageFlagValues> messageFlags)
  
  {
      // If the message IS a duplicate there will never be a response to it, so we
      // should not wait for one and just immediately send a standalone ack.
      if (messageFlags.Has(MessageFlagValues::kDuplicateMessage))
      {
          ChipLogDetail(ExchangeManager,
                        "Forcing tx of solitary ack for duplicate MessageCounter:" ChipLogFormatMessageCounter
                        " on exchange " ChipLogFormatExchange,
                        messageCounter, ChipLogValueExchange(GetExchangeContext()));
  
          bool wasAckPending = IsAckPending() && mPendingPeerAckMessageCounter != messageCounter;
  
          bool messageCounterWasValid = HasPiggybackAckPending();
  
          // Temporary store currently pending ack message counter (even if there is none).
          uint32_t tempAckMessageCounter = mPendingPeerAckMessageCounter;
  
          SetPendingPeerAckMessageCounter(messageCounter);
          CHIP_ERROR err = SendStandaloneAckMessage();
  
          if (wasAckPending)
          {
              // Restore previously pending ack message counter.
              SetPendingPeerAckMessageCounter(tempAckMessageCounter);
          }
          else if (messageCounterWasValid)
          {
              // Restore the previous value, so later piggybacks will pick it up,
              // but don't set out "ack is pending" state, because we didn't use
              // to have it set.
              mPendingPeerAckMessageCounter = tempAckMessageCounter;
          }
          // Otherwise don't restore the invalid old mPendingPeerAckMessageCounter
          // value, so we preserve the invariant that once we have had an ack
          // pending we always have a valid mPendingPeerAckMessageCounter.
  
          return err;
      }
      // Otherwise, the message IS NOT a duplicate.
  
      if (IsAckPending())
      {
          ChipLogDetail(ExchangeManager,
                        "Pending ack queue full; forcing tx of solitary ack for MessageCounter:" ChipLogFormatMessageCounter
                        " on exchange " ChipLogFormatExchange,
                        mPendingPeerAckMessageCounter, ChipLogValueExchange(GetExchangeContext()));
          // Send the Ack for the currently pending Ack in a SecureChannel::StandaloneAck message.
          ReturnErrorOnFailure(SendStandaloneAckMessage());
      }
  
      // Replace the Pending ack message counter.
      SetPendingPeerAckMessageCounter(messageCounter);
      using namespace System::Clock::Literals;
      mNextAckTime = System::SystemClock().GetMonotonicTimestamp() + CHIP_CONFIG_RMP_DEFAULT_ACK_TIMEOUT;
      return CHIP_NO_ERROR;
  }
  
  CHIP_ERROR ReliableMessageContext::SendStandaloneAckMessage()
  {
      // Allocate a buffer for the null message
      System::PacketBufferHandle msgBuf = MessagePacketBuffer::New(0);
      if (msgBuf.IsNull())
      {
          return CHIP_ERROR_NO_MEMORY;
      }
  
      // Send the null message
      ChipLogDetail(ExchangeManager,
                    "Sending Standalone Ack for MessageCounter:" ChipLogFormatMessageCounter " on exchange " ChipLogFormatExchange,
                    mPendingPeerAckMessageCounter, ChipLogValueExchange(GetExchangeContext()));
  
      CHIP_ERROR err = GetExchangeContext()->SendMessage(Protocols::SecureChannel::MsgType::StandaloneAck, std::move(msgBuf),
                                                         BitFlags<SendMessageFlags>{ SendMessageFlags::kNoAutoRequestAck });
      if (IsSendErrorNonCritical(err))
      {
          ChipLogError(ExchangeManager,
                       "Non-crit err %" CHIP_ERROR_FORMAT " sending solitary ack for MessageCounter:" ChipLogFormatMessageCounter
                       " on exchange " ChipLogFormatExchange,
                       err.Format(), mPendingPeerAckMessageCounter, ChipLogValueExchange(GetExchangeContext()));
          return CHIP_NO_ERROR;
      }
      if (err != CHIP_NO_ERROR)
      {
          ChipLogError(ExchangeManager,
                       "Failed to send Solitary ack for MessageCounter:" ChipLogFormatMessageCounter
                       " on exchange " ChipLogFormatExchange ":%" CHIP_ERROR_FORMAT,
                       mPendingPeerAckMessageCounter, ChipLogValueExchange(GetExchangeContext()), err.Format());
      }
  
      return err;
  }
  
  void ReliableMessageContext::SetPendingPeerAckMessageCounter(uint32_t aPeerAckMessageCounter)
  {
      mPendingPeerAckMessageCounter = aPeerAckMessageCounter;
      SetAckPending(true);
      mFlags.Set(Flags::kFlagAckMessageCounterIsValid);
  }
  
  } // namespace Messaging
  } // namespace chip
  