/* * * Copyright (c) 2020 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. */ #include "TimedHandler.h" #include #include #include #include #include #include #include namespace chip { namespace app { CHIP_ERROR TimedHandler::OnMessageReceived(Messaging::ExchangeContext * aExchangeContext, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) { using namespace Protocols::InteractionModel; if (aExchangeContext->IsGroupExchangeContext()) { // Timed interactions are always supposed to be unicast. Nothing else // to do here; exchange will close and we'll free ourselves. ChipLogError(DataManagement, "Dropping Timed Request on group exchange " ChipLogFormatExchange, ChipLogValueExchange(aExchangeContext)); return CHIP_NO_ERROR; } if (mState == State::kExpectingTimedAction) { // We were just created; our caller should have done this only if it's // dealing with a Timed Request message. VerifyOrDie(aPayloadHeader.HasMessageType(MsgType::TimedRequest)); mState = State::kReceivedTimedAction; CHIP_ERROR err = HandleTimedRequestAction(aExchangeContext, aPayloadHeader, std::move(aPayload)); if (err != CHIP_NO_ERROR) { ChipLogError(DataManagement, "Failed to parse Timed Request action: handler %p exchange " ChipLogFormatExchange, this, ChipLogValueExchange(aExchangeContext)); StatusResponse::Send(Status::InvalidAction, aExchangeContext, /* aExpectResponse = */ false); } return err; } if (mState == State::kExpectingFollowingAction) { System::Clock::Timestamp now = System::SystemClock().GetMonotonicTimestamp(); ChipLogDetail(DataManagement, "Timed following action arrived at 0x" ChipLogFormatX64 ": handler %p exchange " ChipLogFormatExchange, ChipLogValueX64(now.count()), this, ChipLogValueExchange(aExchangeContext)); if (now > mTimeLimit) { ChipLogError(DataManagement, "Timeout expired: handler %p exchange " ChipLogFormatExchange, this, ChipLogValueExchange(aExchangeContext)); return StatusResponse::Send(Status::Timeout, aExchangeContext, /* aExpectResponse = */ false); } if (aPayloadHeader.HasMessageType(MsgType::InvokeCommandRequest)) { ChipLogDetail(DataManagement, "Handing timed invoke to IM engine: handler %p exchange " ChipLogFormatExchange, this, ChipLogValueExchange(aExchangeContext)); mDelegate->OnTimedInvoke(this, aExchangeContext, aPayloadHeader, std::move(aPayload)); return CHIP_NO_ERROR; } if (aPayloadHeader.HasMessageType(MsgType::WriteRequest)) { ChipLogDetail(DataManagement, "Handing timed write to IM engine: handler %p exchange " ChipLogFormatExchange, this, ChipLogValueExchange(aExchangeContext)); mDelegate->OnTimedWrite(this, aExchangeContext, aPayloadHeader, std::move(aPayload)); return CHIP_NO_ERROR; } } // Not an expected message. Send an error response. The exchange will // close when we return. ChipLogError(DataManagement, "Unexpected unknown message in tiemd interaction: handler %p exchange " ChipLogFormatExchange, this, ChipLogValueExchange(aExchangeContext)); return StatusResponse::Send(Status::InvalidAction, aExchangeContext, /* aExpectResponse = */ false); } void TimedHandler::OnExchangeClosing(Messaging::ExchangeContext *) { mDelegate->OnTimedInteractionFailed(this); } CHIP_ERROR TimedHandler::HandleTimedRequestAction(Messaging::ExchangeContext * aExchangeContext, const PayloadHeader & aPayloadHeader, System::PacketBufferHandle && aPayload) { using namespace Protocols::InteractionModel; System::PacketBufferTLVReader reader; reader.Init(std::move(aPayload)); TimedRequestMessage::Parser parser; ReturnErrorOnFailure(parser.Init(reader)); #if CHIP_CONFIG_IM_PRETTY_PRINT parser.PrettyPrint(); #endif uint16_t timeoutMs; ReturnErrorOnFailure(parser.GetTimeoutMs(&timeoutMs)); ReturnErrorOnFailure(parser.ExitContainer()); ChipLogDetail(DataManagement, "Got Timed Request with timeout %u: handler %p exchange " ChipLogFormatExchange, timeoutMs, this, ChipLogValueExchange(aExchangeContext)); // Use at least our default IM timeout, because if we close our exchange as // soon as we know the delay has passed we won't be able to send the // UNSUPPORTED_ACCESS status code the spec tells us to send (and in fact // will send nothing and the other side will have to time out to realize // it's missed its window). auto delay = System::Clock::Milliseconds32(timeoutMs); aExchangeContext->SetResponseTimeout( std::max(delay, aExchangeContext->GetSessionHandle()->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime))); ReturnErrorOnFailure(StatusResponse::Send(Status::Success, aExchangeContext, /* aExpectResponse = */ true)); // Now just wait for the client. mState = State::kExpectingFollowingAction; mTimeLimit = System::SystemClock().GetMonotonicTimestamp() + delay; ChipLogDetail(DataManagement, "Timed Request time limit 0x" ChipLogFormatX64 ": handler %p exchange " ChipLogFormatExchange, ChipLogValueX64(mTimeLimit.count()), this, ChipLogValueExchange(aExchangeContext)); return CHIP_NO_ERROR; } } // namespace app } // namespace chip