From 8f3bc0ec46abf835e5180bbd79f9edec8daf3c29 Mon Sep 17 00:00:00 2001 From: yunhanw Date: Tue, 14 Sep 2021 10:24:47 -0700 Subject: [PATCH] Add IM status response support 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 -- Move protocol code type from uint16_t to uint32_t. For status code change in other IM message, we will do it in another PR. --- src/app/BUILD.gn | 1 + src/app/CommandSender.cpp | 2 +- src/app/InteractionModelDelegate.h | 4 +- src/app/InteractionModelEngine.cpp | 2 +- src/app/MessageDef/StatusElement.cpp | 4 +- src/app/MessageDef/StatusElement.h | 4 +- src/app/MessageDef/StatusResponse.cpp | 103 ++++++++++++ src/app/MessageDef/StatusResponse.h | 77 +++++++++ src/app/ReadClient.cpp | 33 ++-- src/app/ReadClient.h | 3 +- src/app/ReadHandler.cpp | 33 ++-- src/app/ReadHandler.h | 2 +- src/app/WriteClient.cpp | 2 +- src/app/tests/BUILD.gn | 1 + src/app/tests/TestMessageDef.cpp | 2 +- src/app/tests/TestStatusResponse.cpp | 156 ++++++++++++++++++ src/app/tests/TestWriteInteraction.cpp | 2 +- .../tests/integration/chip_im_initiator.cpp | 4 +- src/controller/CHIPDeviceController.cpp | 4 +- ...DeviceControllerInteractionModelDelegate.h | 4 +- .../chip/interaction_model/Delegate.cpp | 4 +- .../python/chip/interaction_model/Delegate.h | 12 +- src/protocols/interaction_model/Constants.h | 3 +- 23 files changed, 406 insertions(+), 56 deletions(-) create mode 100644 src/app/MessageDef/StatusResponse.cpp create mode 100644 src/app/MessageDef/StatusResponse.h create mode 100644 src/app/tests/TestStatusResponse.cpp diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 87e6e6bd33e75c..34fb9a0bc16173 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -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", diff --git a/src/app/CommandSender.cpp b/src/app/CommandSender.cpp index 90e7914448cb01..fbcf155ade4c99 100644 --- a/src/app/CommandSender.cpp +++ b/src/app/CommandSender.cpp @@ -121,7 +121,7 @@ CHIP_ERROR CommandSender::ProcessCommandDataElement(CommandDataElement::Parser & chip::EndpointId endpointId; Protocols::SecureChannel::GeneralStatusCode generalCode = Protocols::SecureChannel::GeneralStatusCode::kSuccess; uint32_t protocolId = 0; - uint16_t protocolCode = 0; + uint32_t protocolCode = 0; StatusElement::Parser statusElementParser; mCommandIndex++; diff --git a/src/app/InteractionModelDelegate.h b/src/app/InteractionModelDelegate.h index 9068a5c2e3c15c..e2c802dc333258 100644 --- a/src/app/InteractionModelDelegate.h +++ b/src/app/InteractionModelDelegate.h @@ -116,7 +116,7 @@ class InteractionModelDelegate */ virtual CHIP_ERROR CommandResponseStatus(const CommandSender * apCommandSender, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, - const uint32_t aProtocolId, const uint16_t aProtocolCode, EndpointId aEndpointId, + const uint32_t aProtocolId, const uint32_t aProtocolCode, EndpointId aEndpointId, const ClusterId aClusterId, CommandId aCommandId, uint8_t aCommandIndex) { return CHIP_ERROR_NOT_IMPLEMENTED; @@ -163,7 +163,7 @@ class InteractionModelDelegate */ virtual CHIP_ERROR WriteResponseStatus(const WriteClient * apWriteClient, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, - const uint32_t aProtocolId, const uint16_t aProtocolCode, + const uint32_t aProtocolId, const uint32_t aProtocolCode, AttributePathParams & aAttributePathParams, uint8_t aAttributeIndex) { return CHIP_ERROR_NOT_IMPLEMENTED; diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index bda1188f806f47..45e3477c896dd1 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -187,7 +187,7 @@ CHIP_ERROR InteractionModelEngine::OnUnknownMsgType(Messaging::ExchangeContext * ChipLogDetail(InteractionModel, "Msg type %d not supported", aPayloadHeader.GetMessageType()); // Todo: Add status report - // err = SendStatusReport(ec, kChipProfile_Common, kStatus_UnsupportedMessage); + // err = SendStatusResponse(ec, kChipProfile_Common, kStatus_UnsupportedMessage); // SuccessOrExit(err); apExchangeContext = nullptr; diff --git a/src/app/MessageDef/StatusElement.cpp b/src/app/MessageDef/StatusElement.cpp index 55ee6b04d1edea..0c5ed50f2df805 100644 --- a/src/app/MessageDef/StatusElement.cpp +++ b/src/app/MessageDef/StatusElement.cpp @@ -51,7 +51,7 @@ CHIP_ERROR StatusElement::Parser::Init(const chip::TLV::TLVReader & aReader) } CHIP_ERROR StatusElement::Parser::DecodeStatusElement(Protocols::SecureChannel::GeneralStatusCode * apGeneralCode, - uint32_t * apProtocolId, uint16_t * apProtocolCode) const + uint32_t * apProtocolId, uint32_t * apProtocolCode) const { CHIP_ERROR err = CHIP_NO_ERROR; chip::TLV::TLVReader lReader; @@ -188,7 +188,7 @@ CHIP_ERROR StatusElement::Builder::Init(chip::TLV::TLVWriter * const apWriter, c } StatusElement::Builder & StatusElement::Builder::EncodeStatusElement(const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, - const uint32_t aProtocolId, const uint16_t aProtocolCode) + const uint32_t aProtocolId, const uint32_t aProtocolCode) { uint64_t tag = chip::TLV::AnonymousTag; diff --git a/src/app/MessageDef/StatusElement.h b/src/app/MessageDef/StatusElement.h index 5cd3fa301c8408..689c8d0d5690a0 100644 --- a/src/app/MessageDef/StatusElement.h +++ b/src/app/MessageDef/StatusElement.h @@ -86,7 +86,7 @@ class Parser : public ListParser * type. */ CHIP_ERROR DecodeStatusElement(Protocols::SecureChannel::GeneralStatusCode * apGeneralCode, uint32_t * apProtocolId, - uint16_t * apProtocolCode) const; + uint32_t * apProtocolCode) const; }; class Builder : public ListBuilder @@ -124,7 +124,7 @@ class Builder : public ListBuilder * type. */ StatusElement::Builder & EncodeStatusElement(const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, - const uint32_t aProtocolId, const uint16_t aProtocolCode); + const uint32_t aProtocolId, const uint32_t aProtocolCode); /** * @brief Mark the end of this StatusElement diff --git a/src/app/MessageDef/StatusResponse.cpp b/src/app/MessageDef/StatusResponse.cpp new file mode 100644 index 00000000000000..e9b9cd306af494 --- /dev/null +++ b/src/app/MessageDef/StatusResponse.cpp @@ -0,0 +1,103 @@ +/** + * + * 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 +#include + +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; + uint16_t TagPresenceMask = 0; + 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(!(TagPresenceMask & (1 << kCsTag_Status)), CHIP_ERROR_INVALID_TLV_TAG); + TagPresenceMask |= (1 << kCsTag_Status); + VerifyOrReturnLogError(TLV::kTLVType_UnsignedInteger == reader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE); +#if CHIP_DETAIL_LOGGING + { + uint32_t status; + ReturnLogErrorOnFailure(reader.Get(status)); + PRETTY_PRINT("\tStatus = 0x%" PRIx32 ",", status); + } +#endif // CHIP_DETAIL_LOGGING + break; + default: + ReturnLogErrorOnFailure(CHIP_ERROR_INVALID_TLV_TAG); + } + } + PRETTY_PRINT("}"); + PRETTY_PRINT(""); + + if (CHIP_END_OF_TLV == err) + { + const uint16_t RequiredFields = (1 << kCsTag_Status); + + if ((TagPresenceMask & RequiredFields) == RequiredFields) + { + err = CHIP_NO_ERROR; + } + } + ReturnLogErrorOnFailure(err); + return reader.ExitContainer(mOuterContainerType); +} +#endif // CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK + +CHIP_ERROR StatusResponse::Parser::GetStatus(uint32_t * const apStatus) const +{ + return GetUnsignedInteger(kCsTag_Status, apStatus); +} + +CHIP_ERROR StatusResponse::Builder::Init(TLV::TLVWriter * const apWriter) +{ + return InitAnonymousStructure(apWriter); +} + +StatusResponse::Builder & StatusResponse::Builder::Status(const uint32_t aStatus) +{ + // skip if error has already been set + if (mError == CHIP_NO_ERROR) + { + mError = mpWriter->Put(TLV::ContextTag(kCsTag_Status), aStatus); + } + EndOfContainer(); + return *this; +} + +} // namespace app +} // namespace chip diff --git a/src/app/MessageDef/StatusResponse.h b/src/app/MessageDef/StatusResponse.h new file mode 100644 index 00000000000000..23c59fdd8eafca --- /dev/null +++ b/src/app/MessageDef/StatusResponse.h @@ -0,0 +1,77 @@ +/** + * + * 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 +#include +#include +#include +#include +#include + +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(uint32_t * const apStatus) const; +}; + +class Builder : public app::Builder +{ +public: + CHIP_ERROR Init(TLV::TLVWriter * const apWriter); + StatusResponse::Builder & Status(const uint32_t aStatus); +}; +} // namespace StatusResponse +} // namespace app +} // namespace chip diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp index b9dea66a60334f..485fd48bbc6995 100644 --- a/src/app/ReadClient.cpp +++ b/src/app/ReadClient.cpp @@ -25,7 +25,6 @@ #include #include #include -#include namespace chip { namespace app { @@ -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)); + uint32_t statusCode = to_underlying(Protocols::InteractionModel::ProtocolCode::Success); if (aError != CHIP_NO_ERROR) { - generalCode = Protocols::SecureChannel::GeneralStatusCode::kFailure; - protocolCode = to_underlying(Protocols::InteractionModel::ProtocolCode::InvalidSubscription); + statusCode = to_underlying(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()) { @@ -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; @@ -439,7 +438,7 @@ CHIP_ERROR ReadClient::ProcessReportData(System::PacketBufferHandle && aPayload) mpDelegate->ReportProcessed(this); } exit: - SendStatusReport(err); + SendStatusResponse(err); if (!mInitialReport) { mpExchangeCtx = nullptr; diff --git a/src/app/ReadClient.h b/src/app/ReadClient.h index 67d8c9123a9c20..7ed44b7c8d2a9a 100644 --- a/src/app/ReadClient.h +++ b/src/app/ReadClient.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -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; diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 4cb1cbcd64f573..76353262bc755f 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -25,12 +25,12 @@ #include #include #include +#include #include #include #include #include #include -#include namespace chip { namespace app { @@ -120,16 +120,27 @@ 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)); + CHIP_ERROR err = CHIP_NO_ERROR; + uint32_t statusCode = 0; + 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)), +#endif + + err = response.GetStatus(&statusCode); + SuccessOrExit(err); + + ChipLogProgress(DataManagement, "In state %s, receive status response, status code is %" PRIu32, GetStateStr(), statusCode); + VerifyOrExit((statusCode == to_underlying(Protocols::InteractionModel::ProtocolCode::Success)), err = CHIP_ERROR_INVALID_ARGUMENT); switch (mState) { @@ -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 { diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 0bd8b23377493c..68254a40047c5b 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -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; diff --git a/src/app/WriteClient.cpp b/src/app/WriteClient.cpp index 0bdc867c964389..c5e6c2330b2ca8 100644 --- a/src/app/WriteClient.cpp +++ b/src/app/WriteClient.cpp @@ -331,7 +331,7 @@ CHIP_ERROR WriteClient::ProcessAttributeStatusElement(AttributeStatusElement::Pa AttributePath::Parser attributePath; Protocols::SecureChannel::GeneralStatusCode generalCode = Protocols::SecureChannel::GeneralStatusCode::kSuccess; uint32_t protocolId = 0; - uint16_t protocolCode = 0; + uint32_t protocolCode = 0; StatusElement::Parser statusElementParser; AttributePathParams attributePathParams; diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index aafac491c33878..0f9580b9805848 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -33,6 +33,7 @@ chip_test_suite("tests") { "TestMessageDef.cpp", "TestReadInteraction.cpp", "TestReportingEngine.cpp", + "TestStatusResponse.cpp", "TestWriteInteraction.cpp", ] diff --git a/src/app/tests/TestMessageDef.cpp b/src/app/tests/TestMessageDef.cpp index 178f8137d25f47..7f780644836c1a 100644 --- a/src/app/tests/TestMessageDef.cpp +++ b/src/app/tests/TestMessageDef.cpp @@ -351,7 +351,7 @@ void ParseStatusElement(nlTestSuite * apSuite, StatusElement::Parser & aStatusEl chip::Protocols::SecureChannel::GeneralStatusCode generalCode = chip::Protocols::SecureChannel::GeneralStatusCode::kFailure; uint32_t protocolId = 0; - uint16_t protocolCode = 0; + uint32_t protocolCode = 0; #if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK err = aStatusElementParser.CheckSchemaValidity(); diff --git a/src/app/tests/TestStatusResponse.cpp b/src/app/tests/TestStatusResponse.cpp new file mode 100644 index 00000000000000..b8f874be9b19ac --- /dev/null +++ b/src/app/tests/TestStatusResponse.cpp @@ -0,0 +1,156 @@ +/* + * + * 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 a test for CHIP Interaction Model Message Def + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +namespace { + +using namespace chip::app; +constexpr uint32_t statusValue = 1; +constexpr uint32_t invalidStatusValue = 2; + +void BuildStatusResponse(nlTestSuite * apSuite, chip::TLV::TLVWriter & aWriter) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + StatusResponse::Builder statusResponse; + + err = statusResponse.Init(&aWriter); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + statusResponse.Status(statusValue); + NL_TEST_ASSERT(apSuite, statusResponse.GetError() == CHIP_NO_ERROR); +} + +void ParseStatusResponse(nlTestSuite * apSuite, chip::TLV::TLVReader & aReader, bool aTestPositiveCase) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + StatusResponse::Parser statusResponse; + uint32_t status = 0; + + err = statusResponse.Init(aReader); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); +#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK + err = statusResponse.CheckSchemaValidity(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); +#endif + + err = statusResponse.GetStatus(&status); + if (aTestPositiveCase) + { + NL_TEST_ASSERT(apSuite, status == statusValue && err == CHIP_NO_ERROR); + } + else + { + NL_TEST_ASSERT(apSuite, status != invalidStatusValue && err == CHIP_NO_ERROR); + } +} + +void StatusResponsePositiveTest(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::System::PacketBufferTLVWriter writer; + chip::System::PacketBufferTLVReader reader; + writer.Init(chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSize)); + BuildStatusResponse(apSuite, writer); + chip::System::PacketBufferHandle buf; + err = writer.Finalize(&buf); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + reader.Init(std::move(buf)); + err = reader.Next(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + ParseStatusResponse(apSuite, reader, true /*aTestPositiveCase*/); +} + +void StatusResponseNegativeTest(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::System::PacketBufferTLVWriter writer; + chip::System::PacketBufferTLVReader reader; + writer.Init(chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSize)); + BuildStatusResponse(apSuite, writer); + chip::System::PacketBufferHandle buf; + err = writer.Finalize(&buf); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + reader.Init(std::move(buf)); + err = reader.Next(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + ParseStatusResponse(apSuite, reader, false /*aTestPositiveCase*/); +} + +// clang-format off +const nlTest sTests[] = + { + NL_TEST_DEF("StatusResponsePositiveTest", StatusResponsePositiveTest), + NL_TEST_DEF("StatusResponseNegativeTest", StatusResponseNegativeTest), + NL_TEST_SENTINEL() + }; +// clang-format on +} // namespace + +/** + * Set up the test suite. + */ +static int TestSetup(void * inContext) +{ + CHIP_ERROR error = chip::Platform::MemoryInit(); + if (error != CHIP_NO_ERROR) + return FAILURE; + return SUCCESS; +} + +/** + * Tear down the test suite. + */ +static int TestTeardown(void * inContext) +{ + chip::Platform::MemoryShutdown(); + return SUCCESS; +} + +int TestStatusResponse() +{ + // clang-format off + nlTestSuite theSuite = + { + "StatusResponse", + &sTests[0], + TestSetup, + TestTeardown, + }; + // clang-format on + + nlTestRunner(&theSuite, nullptr); + + return (nlTestRunnerStats(&theSuite)); +} + +CHIP_REGISTER_TEST_SUITE(TestStatusResponse) diff --git a/src/app/tests/TestWriteInteraction.cpp b/src/app/tests/TestWriteInteraction.cpp index 9d8802a6c85d17..3120f12f792c9a 100644 --- a/src/app/tests/TestWriteInteraction.cpp +++ b/src/app/tests/TestWriteInteraction.cpp @@ -270,7 +270,7 @@ class RoundtripDelegate : public chip::app::InteractionModelDelegate public: CHIP_ERROR WriteResponseStatus(const WriteClient * apWriteClient, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, const uint32_t aProtocolId, - const uint16_t aProtocolCode, AttributePathParams & aAttributePathParams, + const uint32_t aProtocolCode, AttributePathParams & aAttributePathParams, uint8_t aCommandIndex) override { mGotResponse = true; diff --git a/src/app/tests/integration/chip_im_initiator.cpp b/src/app/tests/integration/chip_im_initiator.cpp index 7c1a3ce2786fc9..19b367def025bb 100644 --- a/src/app/tests/integration/chip_im_initiator.cpp +++ b/src/app/tests/integration/chip_im_initiator.cpp @@ -576,13 +576,13 @@ class MockInteractionModelApp : public chip::app::InteractionModelDelegate } CHIP_ERROR CommandResponseStatus(const chip::app::CommandSender * apCommandSender, const chip::Protocols::SecureChannel::GeneralStatusCode aGeneralCode, - const uint32_t aProtocolId, const uint16_t aProtocolCode, const chip::EndpointId aEndpointId, + const uint32_t aProtocolId, const uint32_t aProtocolCode, const chip::EndpointId aEndpointId, const chip::ClusterId aClusterId, const chip::CommandId aCommandId, uint8_t aCommandIndex) override { printf("CommandResponseStatus with GeneralCode %d, ProtocolId %d, ProtocolCode %d, EndpointId %d, ClusterId %d, CommandId " "%d, CommandIndex %d", - static_cast(aGeneralCode), aProtocolId, aProtocolCode, aEndpointId, aClusterId, aCommandId, aCommandIndex); + static_cast(aGeneralCode), aProtocolId, aProtocolCode, aEndpointId, aClusterId, aCommandId, aCommandIndex); gLastCommandResult = (aGeneralCode == chip::Protocols::SecureChannel::GeneralStatusCode::kSuccess && aProtocolCode == 0) ? TestCommandResult::kSuccess : TestCommandResult::kFailure; diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 7186c53e4f87f3..75a12503f069a3 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -1582,7 +1582,7 @@ void DeviceCommissioner::OnNodeDiscoveryComplete(const chip::Mdns::DiscoveredNod CHIP_ERROR DeviceControllerInteractionModelDelegate::CommandResponseStatus( const app::CommandSender * apCommandSender, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, - const uint32_t aProtocolId, const uint16_t aProtocolCode, chip::EndpointId aEndpointId, const chip::ClusterId aClusterId, + const uint32_t aProtocolId, const uint32_t aProtocolCode, chip::EndpointId aEndpointId, const chip::ClusterId aClusterId, chip::CommandId aCommandId, uint8_t aCommandIndex) { // Generally IM has more detailed errors than ember library, here we always use the, the actual handling of the @@ -1658,7 +1658,7 @@ CHIP_ERROR DeviceControllerInteractionModelDelegate::ReadDone(const app::ReadCli CHIP_ERROR DeviceControllerInteractionModelDelegate::WriteResponseStatus( const app::WriteClient * apWriteClient, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, - const uint32_t aProtocolId, const uint16_t aProtocolCode, app::AttributePathParams & aAttributePathParams, + const uint32_t aProtocolId, const uint32_t aProtocolCode, app::AttributePathParams & aAttributePathParams, uint8_t aCommandIndex) { IMWriteResponseCallback(apWriteClient, chip::app::ToEmberAfStatus(Protocols::InteractionModel::ProtocolCode(aProtocolCode))); diff --git a/src/controller/DeviceControllerInteractionModelDelegate.h b/src/controller/DeviceControllerInteractionModelDelegate.h index 1a0c23f6f37bda..4e1b65745cf457 100644 --- a/src/controller/DeviceControllerInteractionModelDelegate.h +++ b/src/controller/DeviceControllerInteractionModelDelegate.h @@ -16,7 +16,7 @@ class DeviceControllerInteractionModelDelegate : public chip::app::InteractionMo public: CHIP_ERROR CommandResponseStatus(const app::CommandSender * apCommandSender, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, const uint32_t aProtocolId, - const uint16_t aProtocolCode, chip::EndpointId aEndpointId, const chip::ClusterId aClusterId, + const uint32_t aProtocolCode, chip::EndpointId aEndpointId, const chip::ClusterId aClusterId, chip::CommandId aCommandId, uint8_t aCommandIndex) override; CHIP_ERROR CommandResponseProtocolError(const app::CommandSender * apCommandSender, uint8_t aCommandIndex) override; @@ -31,7 +31,7 @@ class DeviceControllerInteractionModelDelegate : public chip::app::InteractionMo CHIP_ERROR WriteResponseStatus(const app::WriteClient * apWriteClient, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, const uint32_t aProtocolId, - const uint16_t aProtocolCode, app::AttributePathParams & aAttributePathParams, + const uint32_t aProtocolCode, app::AttributePathParams & aAttributePathParams, uint8_t aCommandIndex) override; CHIP_ERROR WriteResponseProtocolError(const app::WriteClient * apWriteClient, uint8_t aAttributeIndex) override; diff --git a/src/controller/python/chip/interaction_model/Delegate.cpp b/src/controller/python/chip/interaction_model/Delegate.cpp index c65bdd151d3e18..7a83fd8bf58ca6 100644 --- a/src/controller/python/chip/interaction_model/Delegate.cpp +++ b/src/controller/python/chip/interaction_model/Delegate.cpp @@ -34,7 +34,7 @@ PythonInteractionModelDelegate gPythonInteractionModelDelegate; CHIP_ERROR PythonInteractionModelDelegate::CommandResponseStatus(const CommandSender * apCommandSender, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, - const uint32_t aProtocolId, const uint16_t aProtocolCode, + const uint32_t aProtocolId, const uint32_t aProtocolCode, chip::EndpointId aEndpointId, const chip::ClusterId aClusterId, chip::CommandId aCommandId, uint8_t aCommandIndex) { @@ -82,7 +82,7 @@ CHIP_ERROR PythonInteractionModelDelegate::CommandResponseProcessed(const app::C CHIP_ERROR PythonInteractionModelDelegate::WriteResponseStatus(const app::WriteClient * apWriteClient, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, - const uint32_t aProtocolId, const uint16_t aProtocolCode, + const uint32_t aProtocolId, const uint32_t aProtocolCode, app::AttributePathParams & aAttributePathParams, uint8_t aCommandIndex) { diff --git a/src/controller/python/chip/interaction_model/Delegate.h b/src/controller/python/chip/interaction_model/Delegate.h index ed34a7aa629aee..021199ce14ed0c 100644 --- a/src/controller/python/chip/interaction_model/Delegate.h +++ b/src/controller/python/chip/interaction_model/Delegate.h @@ -30,7 +30,7 @@ namespace Controller { struct __attribute__((packed)) CommandStatus { uint32_t protocolId; - uint16_t protocolCode; + uint32_t protocolCode; chip::EndpointId endpointId; chip::ClusterId clusterId; chip::CommandId commandId; @@ -40,7 +40,7 @@ struct __attribute__((packed)) CommandStatus static_assert(std::is_same::value && std::is_same::value && std::is_same::value, "Members in CommandStatus does not match interaction_model/delegate.py"); -static_assert(sizeof(CommandStatus) == 4 + 2 + 2 + 4 + 4 + 1, "Size of CommandStatus might contain padding"); +static_assert(sizeof(CommandStatus) == 4 + 4 + 2 + 4 + 4 + 1, "Size of CommandStatus might contain padding"); struct __attribute__((packed)) AttributePath { @@ -59,7 +59,7 @@ struct __attribute__((packed)) AttributeWriteStatus chip::NodeId nodeId; uint64_t appIdentifier; uint32_t protocolId; - uint16_t protocolCode; + uint32_t protocolCode; chip::EndpointId endpointId; chip::ClusterId clusterId; chip::FieldId fieldId; @@ -67,7 +67,7 @@ struct __attribute__((packed)) AttributeWriteStatus static_assert(std::is_same::value && std::is_same::value && std::is_same::value, "Members in AttributeWriteStatus does not match interaction_model/delegate.py"); -static_assert(sizeof(AttributeWriteStatus) == 8 + 8 + 4 + 2 + 2 + 4 + 4, "Size of AttributeWriteStatus might contain padding"); +static_assert(sizeof(AttributeWriteStatus) == 8 + 8 + 4 + 4 + 2 + 4 + 4, "Size of AttributeWriteStatus might contain padding"); extern "C" { typedef void (*PythonInteractionModelDelegate_OnCommandResponseStatusCodeReceivedFunct)(uint64_t commandSenderPtr, @@ -97,7 +97,7 @@ class PythonInteractionModelDelegate : public chip::Controller::DeviceController public: CHIP_ERROR CommandResponseStatus(const app::CommandSender * apCommandSender, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, const uint32_t aProtocolId, - const uint16_t aProtocolCode, chip::EndpointId aEndpointId, const chip::ClusterId aClusterId, + const uint32_t aProtocolCode, chip::EndpointId aEndpointId, const chip::ClusterId aClusterId, chip::CommandId aCommandId, uint8_t aCommandIndex) override; CHIP_ERROR CommandResponseProtocolError(const app::CommandSender * apCommandSender, uint8_t aCommandIndex) override; @@ -108,7 +108,7 @@ class PythonInteractionModelDelegate : public chip::Controller::DeviceController CHIP_ERROR WriteResponseStatus(const app::WriteClient * apWriteClient, const Protocols::SecureChannel::GeneralStatusCode aGeneralCode, const uint32_t aProtocolId, - const uint16_t aProtocolCode, app::AttributePathParams & aAttributePathParams, + const uint32_t aProtocolCode, app::AttributePathParams & aAttributePathParams, uint8_t aCommandIndex) override; void OnReportData(const app::ReadClient * apReadClient, const app::ClusterInfo & aPath, TLV::TLVReader * apData, diff --git a/src/protocols/interaction_model/Constants.h b/src/protocols/interaction_model/Constants.h index 0adc286ce1a87e..700e7d02904ebe 100644 --- a/src/protocols/interaction_model/Constants.h +++ b/src/protocols/interaction_model/Constants.h @@ -53,6 +53,7 @@ constexpr uint16_t kVersion = 0; */ enum class MsgType : uint8_t { + StatusResponse = 0x01, ReadRequest = 0x02, SubscribeRequest = 0x03, SubscribeResponse = 0x04, @@ -65,7 +66,7 @@ enum class MsgType : uint8_t }; // This table comes from the IM's "Status Code Table" section from the Interaction Model spec. -enum class ProtocolCode : uint16_t +enum class ProtocolCode : uint32_t { Success = 0x0, Failure = 0x01,