Skip to content

Commit

Permalink
Add IM status response support (project-chip#9706)
Browse files Browse the repository at this point in the history
Problems:
Interaction Model spec has introduced status reponse message and used it
for IM Read/Subscribe. Currently IM read/subscribe code still use status
report from secure channel, we need to update it with status reponse.

Summary of Changes:
-- Add status reponse message builder and parser and test
-- Replace status report with status response message for IM
read/subscribe
  • Loading branch information
yunhanw-google authored Sep 16, 2021
1 parent 966c64b commit 203f9bf
Show file tree
Hide file tree
Showing 10 changed files with 381 additions and 31 deletions.
1 change: 1 addition & 0 deletions src/app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ static_library("app") {
"MessageDef/ReportData.h",
"MessageDef/StatusElement.cpp",
"MessageDef/StatusElement.h",
"MessageDef/StatusResponse.cpp",
"MessageDef/SubscribeRequest.cpp",
"MessageDef/SubscribeResponse.cpp",
"MessageDef/TimedRequest.cpp",
Expand Down
101 changes: 101 additions & 0 deletions src/app/MessageDef/StatusResponse.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
*
* Copyright (c) 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.
*/

#include "StatusResponse.h"
#include "MessageDefHelper.h"

namespace chip {
namespace app {
CHIP_ERROR StatusResponse::Parser::Init(const TLV::TLVReader & aReader)
{
// make a copy of the reader here
mReader.Init(aReader);
VerifyOrReturnLogError(TLV::kTLVType_Structure == mReader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE);
ReturnLogErrorOnFailure(mReader.EnterContainer(mOuterContainerType));
return CHIP_NO_ERROR;
}

#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK
CHIP_ERROR StatusResponse::Parser::CheckSchemaValidity() const
{
CHIP_ERROR err = CHIP_NO_ERROR;
bool statusTagPresence = false;
TLV::TLVReader reader;
PRETTY_PRINT("StatusResponse =");
PRETTY_PRINT("{");

// make a copy of the reader
reader.Init(mReader);

while (CHIP_NO_ERROR == (err = reader.Next()))
{
VerifyOrReturnLogError(TLV::IsContextTag(reader.GetTag()), CHIP_ERROR_INVALID_TLV_TAG);
switch (TLV::TagNumFromTag(reader.GetTag()))
{
case kCsTag_Status:
VerifyOrReturnLogError(!statusTagPresence, CHIP_ERROR_INVALID_TLV_TAG);
statusTagPresence = true;
VerifyOrReturnLogError(TLV::kTLVType_UnsignedInteger == reader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE);
#if CHIP_DETAIL_LOGGING
{
uint16_t status;
ReturnLogErrorOnFailure(reader.Get(status));
PRETTY_PRINT("\tStatus = 0x%" PRIx16 ",", status);
}
#endif // CHIP_DETAIL_LOGGING
break;
default:
ReturnLogErrorOnFailure(CHIP_ERROR_INVALID_TLV_TAG);
}
}
PRETTY_PRINT("}");
PRETTY_PRINT("");

if (CHIP_END_OF_TLV == err && statusTagPresence)
{
err = CHIP_NO_ERROR;
}
ReturnLogErrorOnFailure(err);
return reader.ExitContainer(mOuterContainerType);
}
#endif // CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK

CHIP_ERROR StatusResponse::Parser::GetStatus(Protocols::InteractionModel::ProtocolCode & aStatus) const
{
uint16_t status = 0;
CHIP_ERROR err = GetUnsignedInteger(kCsTag_Status, &status);
aStatus = static_cast<Protocols::InteractionModel::ProtocolCode>(status);
return err;
}

CHIP_ERROR StatusResponse::Builder::Init(TLV::TLVWriter * const apWriter)
{
return InitAnonymousStructure(apWriter);
}

StatusResponse::Builder & StatusResponse::Builder::Status(const Protocols::InteractionModel::ProtocolCode aStatus)
{
// skip if error has already been set
if (mError == CHIP_NO_ERROR)
{
mError = mpWriter->Put(TLV::ContextTag(kCsTag_Status), to_underlying(aStatus));
}
EndOfContainer();
return *this;
}

} // namespace app
} // namespace chip
78 changes: 78 additions & 0 deletions src/app/MessageDef/StatusResponse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
*
* Copyright (c) 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.
*/

#pragma once
#include "Builder.h"
#include "Parser.h"
#include <app/AppBuildConfig.h>
#include <app/util/basic-types.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPTLV.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <protocols/interaction_model/Constants.h>

namespace chip {
namespace app {
namespace StatusResponse {
enum
{
kCsTag_Status = 0,
};

class Parser : public app::Parser
{
public:
/**
* @param [in] aReader A pointer to a TLVReader, which should point to the beginning of this response
*/
CHIP_ERROR Init(const TLV::TLVReader & aReader);
#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK
/**
* @brief Roughly verify the message is correctly formed
* 1) all mandatory tags are present
* 2) all elements have expected data type
* 3) any tag can only appear once
* 4) At the top level of the structure, unknown tags are ignored for forward compatibility
* @note The main use of this function is to print out what we're
* receiving during protocol development and debugging.
* The encoding rule has changed in IM encoding spec so this
* check is only "roughly" conformant now.
*
* @return #CHIP_NO_ERROR on success
*/
CHIP_ERROR CheckSchemaValidity() const;
#endif

/**
* @brief Get Status. Next() must be called before accessing them.
*
* @return #CHIP_NO_ERROR on success
* #CHIP_END_OF_TLV if there is no such element
*/
CHIP_ERROR GetStatus(Protocols::InteractionModel::ProtocolCode & aStatus) const;
};

class Builder : public app::Builder
{
public:
CHIP_ERROR Init(TLV::TLVWriter * const apWriter);
StatusResponse::Builder & Status(const Protocols::InteractionModel::ProtocolCode aStatus);
};
} // namespace StatusResponse
} // namespace app
} // namespace chip
33 changes: 16 additions & 17 deletions src/app/ReadClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include <app/AppBuildConfig.h>
#include <app/InteractionModelEngine.h>
#include <app/ReadClient.h>
#include <protocols/secure_channel/StatusReport.h>

namespace chip {
namespace app {
Expand Down Expand Up @@ -189,25 +188,25 @@ CHIP_ERROR ReadClient::SendReadRequest(ReadPrepareParams & aReadPrepareParams)
return err;
}

CHIP_ERROR ReadClient::SendStatusReport(CHIP_ERROR aError)
CHIP_ERROR ReadClient::SendStatusResponse(CHIP_ERROR aError)
{
Protocols::SecureChannel::GeneralStatusCode generalCode = Protocols::SecureChannel::GeneralStatusCode::kSuccess;
uint32_t protocolId = Protocols::InteractionModel::Id.ToFullyQualifiedSpecForm();
uint16_t protocolCode = to_underlying(Protocols::InteractionModel::ProtocolCode::Success);
VerifyOrReturnLogError(mpExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE);
System::PacketBufferHandle msgBuf = System::PacketBufferHandle::New(kMaxSecureSduLengthBytes);
VerifyOrReturnLogError(!msgBuf.IsNull(), CHIP_ERROR_NO_MEMORY);

System::PacketBufferTLVWriter writer;
writer.Init(std::move(msgBuf));

StatusResponse::Builder response;
ReturnLogErrorOnFailure(response.Init(&writer));
Protocols::InteractionModel::ProtocolCode statusCode = Protocols::InteractionModel::ProtocolCode::Success;
if (aError != CHIP_NO_ERROR)
{
generalCode = Protocols::SecureChannel::GeneralStatusCode::kFailure;
protocolCode = to_underlying(Protocols::InteractionModel::ProtocolCode::InvalidSubscription);
statusCode = Protocols::InteractionModel::ProtocolCode::InvalidSubscription;
}

Protocols::SecureChannel::StatusReport report(generalCode, protocolId, protocolCode);

Encoding::LittleEndian::PacketBufferWriter buf(System::PacketBufferHandle::New(kMaxSecureSduLengthBytes));
report.WriteToBuffer(buf);
System::PacketBufferHandle msgBuf = buf.Finalize();
VerifyOrReturnLogError(!msgBuf.IsNull(), CHIP_ERROR_NO_MEMORY);
response.Status(statusCode);
ReturnLogErrorOnFailure(response.GetError());
ReturnLogErrorOnFailure(writer.Finalize(&msgBuf));
VerifyOrReturnLogError(mpExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE);

if (IsSubscriptionType())
{
Expand All @@ -221,7 +220,7 @@ CHIP_ERROR ReadClient::SendStatusReport(CHIP_ERROR aError)
}
}
ReturnLogErrorOnFailure(
mpExchangeCtx->SendMessage(Protocols::SecureChannel::MsgType::StatusReport, std::move(msgBuf),
mpExchangeCtx->SendMessage(Protocols::InteractionModel::MsgType::StatusResponse, std::move(msgBuf),
Messaging::SendFlags(IsAwaitingSubscribeResponse() ? Messaging::SendMessageFlags::kExpectResponse
: Messaging::SendMessageFlags::kNone)));
return CHIP_NO_ERROR;
Expand Down Expand Up @@ -439,7 +438,7 @@ CHIP_ERROR ReadClient::ProcessReportData(System::PacketBufferHandle && aPayload)
mpDelegate->ReportProcessed(this);
}
exit:
SendStatusReport(err);
SendStatusResponse(err);
if (!mInitialReport)
{
mpExchangeCtx = nullptr;
Expand Down
3 changes: 2 additions & 1 deletion src/app/ReadClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <app/EventPathParams.h>
#include <app/InteractionModelDelegate.h>
#include <app/MessageDef/ReadRequest.h>
#include <app/MessageDef/StatusResponse.h>
#include <app/MessageDef/SubscribeRequest.h>
#include <app/MessageDef/SubscribeResponse.h>
#include <app/ReadPrepareParams.h>
Expand Down Expand Up @@ -95,7 +96,7 @@ class ReadClient : public Messaging::ExchangeDelegate
Messaging::ExchangeContext * GetExchangeContext() const { return mpExchangeCtx; }
bool IsReadType() { return mInteractionType == InteractionType::Read; }
bool IsSubscriptionType() const { return mInteractionType == InteractionType::Subscribe; };
CHIP_ERROR SendStatusReport(CHIP_ERROR aError);
CHIP_ERROR SendStatusResponse(CHIP_ERROR aError);

private:
friend class TestReadInteraction;
Expand Down
35 changes: 23 additions & 12 deletions src/app/ReadHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@
#include <app/AppBuildConfig.h>
#include <app/InteractionModelEngine.h>
#include <app/MessageDef/EventPath.h>
#include <app/MessageDef/StatusResponse.h>
#include <app/MessageDef/SubscribeRequest.h>
#include <app/MessageDef/SubscribeResponse.h>
#include <app/ReadHandler.h>
#include <app/reporting/Engine.h>
#include <lib/support/RandUtils.h>
#include <protocols/secure_channel/StatusReport.h>

namespace chip {
namespace app {
Expand Down Expand Up @@ -120,17 +120,28 @@ CHIP_ERROR ReadHandler::OnReadInitialRequest(System::PacketBufferHandle && aPayl
return err;
}

CHIP_ERROR ReadHandler::OnStatusReport(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload)
CHIP_ERROR ReadHandler::OnStatusResponse(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload)
{
CHIP_ERROR err = CHIP_NO_ERROR;
Protocols::SecureChannel::StatusReport statusReport;
err = statusReport.Parse(std::move(aPayload));
Protocols::InteractionModel::ProtocolCode statusCode;
StatusResponse::Parser response;
System::PacketBufferTLVReader reader;
reader.Init(std::move(aPayload));
reader.Next();
err = response.Init(reader);
SuccessOrExit(err);

#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK
err = response.CheckSchemaValidity();
SuccessOrExit(err);
ChipLogProgress(DataManagement, "in state %s, receive status report, protocol id is %" PRIu32 ", protocol code is %" PRIu16,
GetStateStr(), statusReport.GetProtocolId(), statusReport.GetProtocolCode());
VerifyOrExit((statusReport.GetProtocolId() == Protocols::InteractionModel::Id.ToFullyQualifiedSpecForm()) &&
(statusReport.GetProtocolCode() == to_underlying(Protocols::InteractionModel::ProtocolCode::Success)),
err = CHIP_ERROR_INVALID_ARGUMENT);
#endif

err = response.GetStatus(statusCode);
SuccessOrExit(err);

ChipLogProgress(DataManagement, "In state %s, receive status response, status code is %" PRIu16, GetStateStr(),
to_underlying(statusCode));
VerifyOrExit((statusCode == Protocols::InteractionModel::ProtocolCode::Success), err = CHIP_ERROR_INVALID_ARGUMENT);
switch (mState)
{
case HandlerState::AwaitingReportResponse:
Expand Down Expand Up @@ -201,9 +212,9 @@ CHIP_ERROR ReadHandler::OnMessageReceived(Messaging::ExchangeContext * apExchang
{
CHIP_ERROR err = CHIP_NO_ERROR;

if (aPayloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::StatusReport))
if (aPayloadHeader.HasMessageType(Protocols::InteractionModel::MsgType::StatusResponse))
{
err = OnStatusReport(apExchangeContext, std::move(aPayload));
err = OnStatusResponse(apExchangeContext, std::move(aPayload));
}
else
{
Expand Down Expand Up @@ -477,7 +488,7 @@ CHIP_ERROR ReadHandler::SendSubscribeResponse()
SubscribeResponse::Builder response;
ReturnLogErrorOnFailure(response.Init(&writer));
response.SubscriptionId(mSubscriptionId)
.MinIntervalFloorSeconds(mMaxIntervalCeilingSeconds)
.MinIntervalFloorSeconds(mMinIntervalFloorSeconds)
.MaxIntervalCeilingSeconds(mMaxIntervalCeilingSeconds)
.EndOfSubscribeResponse();
ReturnLogErrorOnFailure(response.GetError());
Expand Down
2 changes: 1 addition & 1 deletion src/app/ReadHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class ReadHandler : public Messaging::ExchangeDelegate
CHIP_ERROR ProcessReadRequest(System::PacketBufferHandle && aPayload);
CHIP_ERROR ProcessAttributePathList(AttributePathList::Parser & aAttributePathListParser);
CHIP_ERROR ProcessEventPathList(EventPathList::Parser & aEventPathListParser);
CHIP_ERROR OnStatusReport(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload);
CHIP_ERROR OnStatusResponse(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload);
CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
System::PacketBufferHandle && aPayload) override;
void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override;
Expand Down
1 change: 1 addition & 0 deletions src/app/tests/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ chip_test_suite("tests") {
"TestMessageDef.cpp",
"TestReadInteraction.cpp",
"TestReportingEngine.cpp",
"TestStatusResponse.cpp",
"TestWriteInteraction.cpp",
]

Expand Down
Loading

0 comments on commit 203f9bf

Please sign in to comment.