| /* |
| * |
| * 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 <app/InteractionModelTimeout.h> |
| #include <app/MessageDef/TimedRequestMessage.h> |
| #include <app/StatusResponse.h> |
| #include <lib/core/TLV.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <system/SystemClock.h> |
| #include <system/TLVPacketBufferBackingStore.h> |
| |
| 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 |