From 068f01ce11e4df82b226aea193acdb06ed27cd3a Mon Sep 17 00:00:00 2001 From: James Swan <122404367+swan-amazon@users.noreply.github.com> Date: Mon, 20 May 2024 21:20:35 +0000 Subject: [PATCH] Add initial feature logic for Terms and Conditions (TC) acknowledgements This commit introduces the initial logic for handling Terms and Conditions (TC) acknowledgements in the General Commissioning cluster. The logic includes support for setting and checking TC acknowledgements and versions during the commissioning process. Changes include: - Handling TC acknowledgements and TC acknowledgement version in the pairing command. - Logic to read TC attributes and check TC acceptance in the General Commissioning server. - Introduction of classes to manage TC acceptance logic. - Initialization and use of TC providers in the server setup. - Addition of a new commissioning stage for TC acknowledgements in the commissioning flow. The feature logic is currently disabled and will be enabled in an example in a subsequent commit. --- .../commands/pairing/PairingCommand.cpp | 13 +- .../commands/pairing/PairingCommand.h | 12 +- .../general-commissioning-server.cpp | 207 +++++++++--- src/app/server/BUILD.gn | 8 +- .../DefaultEnhancedSetupFlowProvider.cpp | 151 +++++++++ .../server/DefaultEnhancedSetupFlowProvider.h | 87 +++++ .../DefaultTermsAndConditionsProvider.cpp | 129 ++++++++ .../DefaultTermsAndConditionsProvider.h | 71 ++++ src/app/server/EnhancedSetupFlowProvider.h | 108 +++++++ src/app/server/Server.cpp | 113 ++++++- src/app/server/Server.h | 94 ++---- src/app/server/TermsAndConditionsProvider.h | 75 +++++ src/app/tests/BUILD.gn | 4 +- .../TestDefaultEnhancedSetupFlowProvider.cpp | 305 ++++++++++++++++++ .../TestDefaultTermsAndConditionsProvider.cpp | 225 +++++++++++++ src/controller/AutoCommissioner.cpp | 4 +- src/controller/CHIPDeviceController.cpp | 40 ++- src/controller/CHIPDeviceController.h | 5 +- src/controller/CommissioningDelegate.h | 22 +- src/lib/support/DefaultStorageKeyAllocator.h | 6 +- 20 files changed, 1550 insertions(+), 129 deletions(-) create mode 100644 src/app/server/DefaultEnhancedSetupFlowProvider.cpp create mode 100644 src/app/server/DefaultEnhancedSetupFlowProvider.h create mode 100644 src/app/server/DefaultTermsAndConditionsProvider.cpp create mode 100644 src/app/server/DefaultTermsAndConditionsProvider.h create mode 100644 src/app/server/EnhancedSetupFlowProvider.h create mode 100644 src/app/server/TermsAndConditionsProvider.h create mode 100644 src/app/tests/TestDefaultEnhancedSetupFlowProvider.cpp create mode 100644 src/app/tests/TestDefaultTermsAndConditionsProvider.cpp diff --git a/examples/chip-tool/commands/pairing/PairingCommand.cpp b/examples/chip-tool/commands/pairing/PairingCommand.cpp index 76c8ac8a58cc19..22e9465efe09a5 100644 --- a/examples/chip-tool/commands/pairing/PairingCommand.cpp +++ b/examples/chip-tool/commands/pairing/PairingCommand.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -120,6 +120,17 @@ CommissioningParameters PairingCommand::GetCommissioningParameters() params.SetCountryCode(CharSpan::fromCharString(mCountryCode.Value())); } + // mTCAcknowledgements and mTCAcknowledgementVersion are optional, but related. When one is missing, default the value to 0, to + // increase the test tools ability to test the applications. + if (mTCAcknowledgements.HasValue() || mTCAcknowledgementVersion.HasValue()) + { + TermsAndConditionsAcknowledgement termsAndConditionsAcknowledgement = { + .acceptedTermsAndConditions = mTCAcknowledgements.ValueOr(0), + .acceptedTermsAndConditionsVersion = mTCAcknowledgementVersion.ValueOr(0), + }; + params.SetTermsAndConditionsAcknowledgement(termsAndConditionsAcknowledgement); + } + // mTimeZoneList is an optional argument managed by TypedComplexArgument mComplex_TimeZones. // Since optional Complex arguments are not currently supported via the class, // we will use mTimeZoneList.data() value to determine if the argument was provided. diff --git a/examples/chip-tool/commands/pairing/PairingCommand.h b/examples/chip-tool/commands/pairing/PairingCommand.h index aaa8dc714e1017..c08327465d7f86 100644 --- a/examples/chip-tool/commands/pairing/PairingCommand.h +++ b/examples/chip-tool/commands/pairing/PairingCommand.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -182,6 +182,14 @@ class PairingCommand : public CHIPCommand, AddArgument("dst-offset", &mComplex_DSTOffsets, "DSTOffset list to use when setting Time Synchronization cluster's DSTOffset attribute", Argument::kOptional); + + AddArgument("tc-acknowledgements", 0, UINT16_MAX, &mTCAcknowledgements, + "Terms and Conditions acknowledgements to use to set the General Commissioning cluster's TC " + "Acknowledgements bit-field"); + + AddArgument("tc-acknowledgements-version", 0, UINT16_MAX, &mTCAcknowledgementVersion, + "Terms and Conditions acknowledgement version to use to set the General Commissioning cluster's TC " + "Acknowledgement version"); } AddArgument("timeout", 0, UINT16_MAX, &mTimeout); @@ -238,6 +246,8 @@ class PairingCommand : public CHIPCommand, chip::Optional mICDSymmetricKey; chip::Optional mICDMonitoredSubject; chip::Optional mICDStayActiveDurationMsec; + chip::Optional mTCAcknowledgements; + chip::Optional mTCAcknowledgementVersion; chip::app::DataModel::List mTimeZoneList; TypedComplexArgument> mComplex_TimeZones; diff --git a/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp b/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp index 776441ffc9ae75..61ac076e120ef0 100644 --- a/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp +++ b/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2024 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. @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include using namespace chip; @@ -57,6 +59,55 @@ using Transport::Session; namespace { +template +static CHIP_ERROR ReadInternal(const T * const provider, CHIP_ERROR (T::*getter)(K &) const, AttributeValueEncoder & aEncoder) +{ + K data; + + if (nullptr == provider) + { + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + } + + CHIP_ERROR err = (provider->*getter)(data); + if (err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE) + { + data = 0; + } + else if (err != CHIP_NO_ERROR) + { + return err; + } + + return aEncoder.Encode(data); +} + +template +static CHIP_ERROR ReadInternal(Provider * provider, CHIP_ERROR (Provider::*getter)(T &), AttributeValueEncoder & aEncoder) +{ + const Provider * constProvider = provider; + CHIP_ERROR (Provider::*constGetter)(T &) const = reinterpret_cast(getter); + return ReadInternal(constProvider, constGetter, aEncoder); +} + +template +static CHIP_ERROR ReadInternal(const T & provider, CHIP_ERROR (T::*getter)(K &) const, AttributeValueEncoder & aEncoder) +{ + return ReadInternal(&provider, getter, aEncoder); +} + +template +static CHIP_ERROR ReadInternal(T & provider, CHIP_ERROR (T::*getter)(K &), AttributeValueEncoder & aEncoder) +{ + return ReadInternal(&provider, getter, aEncoder); +} + +template +static CHIP_ERROR ReadIfSupported(Args &&... args) +{ + return ReadInternal(std::forward(args)...); +} + class GeneralCommissioningAttrAccess : public AttributeAccessInterface { public: @@ -66,7 +117,6 @@ class GeneralCommissioningAttrAccess : public AttributeAccessInterface CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; private: - CHIP_ERROR ReadIfSupported(CHIP_ERROR (ConfigurationManager::*getter)(uint8_t &), AttributeValueEncoder & aEncoder); CHIP_ERROR ReadBasicCommissioningInfo(AttributeValueEncoder & aEncoder); CHIP_ERROR ReadSupportsConcurrentConnection(AttributeValueEncoder & aEncoder); }; @@ -84,10 +134,10 @@ CHIP_ERROR GeneralCommissioningAttrAccess::Read(const ConcreteReadAttributePath switch (aPath.mAttributeId) { case RegulatoryConfig::Id: { - return ReadIfSupported(&ConfigurationManager::GetRegulatoryLocation, aEncoder); + return ReadIfSupported(DeviceLayer::ConfigurationMgr(), &ConfigurationManager::GetRegulatoryLocation, aEncoder); } case LocationCapability::Id: { - return ReadIfSupported(&ConfigurationManager::GetLocationCapability, aEncoder); + return ReadIfSupported(DeviceLayer::ConfigurationMgr(), &ConfigurationManager::GetLocationCapability, aEncoder); } case BasicCommissioningInfo::Id: { return ReadBasicCommissioningInfo(aEncoder); @@ -95,28 +145,32 @@ CHIP_ERROR GeneralCommissioningAttrAccess::Read(const ConcreteReadAttributePath case SupportsConcurrentConnection::Id: { return ReadSupportsConcurrentConnection(aEncoder); } - default: { - break; +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + case TCAcceptedVersion::Id: { + auto provider = Server::GetInstance().GetEnhancedSetupFlowProvider(); + auto getter = &EnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgementsVersion; + return ReadIfSupported(provider, getter, aEncoder); } + case TCMinRequiredVersion::Id: { + auto provider = Server::GetInstance().GetEnhancedSetupFlowProvider(); + auto getter = &EnhancedSetupFlowProvider::GetTermsAndConditionsRequiredAcknowledgementsVersion; + return ReadIfSupported(provider, getter, aEncoder); } - return CHIP_NO_ERROR; -} - -CHIP_ERROR GeneralCommissioningAttrAccess::ReadIfSupported(CHIP_ERROR (ConfigurationManager::*getter)(uint8_t &), - AttributeValueEncoder & aEncoder) -{ - uint8_t data; - CHIP_ERROR err = (DeviceLayer::ConfigurationMgr().*getter)(data); - if (err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE) - { - data = 0; + case TCAcknowledgements::Id: { + auto provider = Server::GetInstance().GetEnhancedSetupFlowProvider(); + auto getter = &EnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgements; + return ReadIfSupported(provider, getter, aEncoder); } - else if (err != CHIP_NO_ERROR) - { - return err; + case TCAcknowledgementsRequired::Id: { + auto provider = Server::GetInstance().GetEnhancedSetupFlowProvider(); + auto getter = &EnhancedSetupFlowProvider::GetTermsAndConditionsRequiredAcknowledgements; + return ReadIfSupported(provider, getter, aEncoder); } - - return aEncoder.Encode(data); +#endif + default: + break; + } + return CHIP_NO_ERROR; } CHIP_ERROR GeneralCommissioningAttrAccess::ReadBasicCommissioningInfo(AttributeValueEncoder & aEncoder) @@ -214,9 +268,12 @@ bool emberAfGeneralCommissioningClusterCommissioningCompleteCallback( { MATTER_TRACE_SCOPE("CommissioningComplete", "GeneralCommissioning"); - DeviceControlServer * devCtrl = &DeviceLayer::DeviceControlServer::DeviceControlSvr(); - auto & failSafe = Server::GetInstance().GetFailSafeContext(); - auto & fabricTable = Server::GetInstance().GetFabricTable(); +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + EnhancedSetupFlowProvider * enhancedSetupFlowProvider = Server::GetInstance().GetEnhancedSetupFlowProvider(); +#endif + DeviceControlServer * const devCtrl = &DeviceLayer::DeviceControlServer::DeviceControlSvr(); + auto & failSafe = Server::GetInstance().GetFailSafeContext(); + auto & fabricTable = Server::GetInstance().GetFabricTable(); ChipLogProgress(FailSafe, "GeneralCommissioning: Received CommissioningComplete"); @@ -239,34 +296,64 @@ bool emberAfGeneralCommissioningClusterCommissioningCompleteCallback( } else { - if (failSafe.NocCommandHasBeenInvoked()) + CHIP_ERROR err; + +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + + uint16_t termsAndConditionsAcceptedAcknowledgements; + bool hasTermsAndConditionsRequiredAcknowledgementsBeenAccepted; + bool hasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted; + + err = enhancedSetupFlowProvider->GetTermsAndConditionsAcceptedAcknowledgements( + termsAndConditionsAcceptedAcknowledgements); + CheckSuccess(err, Failure); + + err = enhancedSetupFlowProvider->HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted( + hasTermsAndConditionsRequiredAcknowledgementsBeenAccepted); + CheckSuccess(err, Failure); + + err = enhancedSetupFlowProvider->HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted( + hasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted); + CheckSuccess(err, Failure); + + if (!hasTermsAndConditionsRequiredAcknowledgementsBeenAccepted) { - CHIP_ERROR err = fabricTable.CommitPendingFabricData(); - if (err != CHIP_NO_ERROR) - { - // No need to revert on error: CommitPendingFabricData always reverts if not fully successful. - ChipLogError(FailSafe, "GeneralCommissioning: Failed to commit pending fabric data: %" CHIP_ERROR_FORMAT, - err.Format()); - } - else + ChipLogError(AppServer, "Required terms and conditions have not been accepted"); + Breadcrumb::Set(commandPath.mEndpointId, 0); + response.errorCode = (0 == termsAndConditionsAcceptedAcknowledgements) + ? CommissioningErrorEnum::kTCAcknowledgementsNotReceived + : CommissioningErrorEnum::kRequiredTCNotAccepted; + } + + else if (!hasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted) + { + ChipLogError(AppServer, "Minimum terms and conditions version has not been accepted"); + Breadcrumb::Set(commandPath.mEndpointId, 0); + response.errorCode = CommissioningErrorEnum::kTCMinVersionNotMet; + } + + else +#endif + { + if (failSafe.NocCommandHasBeenInvoked()) { + err = fabricTable.CommitPendingFabricData(); + CheckSuccess(err, Failure); ChipLogProgress(FailSafe, "GeneralCommissioning: Successfully commited pending fabric data"); } - CheckSuccess(err, Failure); - } - /* - * Pass fabric of commissioner to DeviceControlSvr. - * This allows device to send messages back to commissioner. - * Once bindings are implemented, this may no longer be needed. - */ - failSafe.DisarmFailSafe(); - CheckSuccess( - devCtrl->PostCommissioningCompleteEvent(handle->AsSecureSession()->GetPeerNodeId(), handle->GetFabricIndex()), - Failure); + /* + * Pass fabric of commissioner to DeviceControlSvr. + * This allows device to send messages back to commissioner. + * Once bindings are implemented, this may no longer be needed. + */ + failSafe.DisarmFailSafe(); + err = devCtrl->PostCommissioningCompleteEvent(handle->AsSecureSession()->GetPeerNodeId(), handle->GetFabricIndex()); + CheckSuccess(err, Failure); - Breadcrumb::Set(commandPath.mEndpointId, 0); - response.errorCode = CommissioningErrorEnum::kOk; + Breadcrumb::Set(commandPath.mEndpointId, 0); + response.errorCode = CommissioningErrorEnum::kOk; + } } } @@ -328,13 +415,37 @@ bool emberAfGeneralCommissioningClusterSetRegulatoryConfigCallback(app::CommandH return true; } +bool emberAfGeneralCommissioningClusterSetTCAcknowledgementsCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::GeneralCommissioning::Commands::SetTCAcknowledgements::DecodableType & commandData) +{ +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + MATTER_TRACE_SCOPE("SetTCAcknowledgements", "GeneralCommissioning"); + Commands::SetTCAcknowledgementsResponse::Type response; + EnhancedSetupFlowProvider * const enhancedSetupFlowProvider = Server::GetInstance().GetEnhancedSetupFlowProvider(); + uint16_t acknowledgements = commandData.TCUserResponse; + uint16_t acknowledgementsVersion = commandData.TCVersion; + CheckSuccess(enhancedSetupFlowProvider->SetTermsAndConditionsAcceptance(acknowledgements, acknowledgementsVersion), Failure); + response.errorCode = CommissioningErrorEnum::kOk; + + commandObj->AddResponse(commandPath, response); +#endif + return true; +} + namespace { void OnPlatformEventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) { - if (event->Type == DeviceLayer::DeviceEventType::kFailSafeTimerExpired) + switch (event->Type) { + case DeviceLayer::DeviceEventType::kFailSafeTimerExpired: { // Spec says to reset Breadcrumb attribute to 0. Breadcrumb::Set(0, 0); + break; + } + default: { + break; + } } } diff --git a/src/app/server/BUILD.gn b/src/app/server/BUILD.gn index 51a259c86d2552..8b276354fe33fd 100644 --- a/src/app/server/BUILD.gn +++ b/src/app/server/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2020 Project CHIP Authors +# Copyright (c) 2020-2024 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. @@ -36,14 +36,20 @@ static_library("server") { "CommissioningWindowManager.h", "DefaultAclStorage.cpp", "DefaultAclStorage.h", + "DefaultEnhancedSetupFlowProvider.cpp", + "DefaultEnhancedSetupFlowProvider.h", + "DefaultTermsAndConditionsProvider.cpp", + "DefaultTermsAndConditionsProvider.h", "Dnssd.cpp", "Dnssd.h", "EchoHandler.cpp", "EchoHandler.h", + "EnhancedSetupFlowProvider.h", "OnboardingCodesUtil.cpp", "OnboardingCodesUtil.h", "Server.cpp", "Server.h", + "TermsAndConditionsProvider.h", ] public_configs = [ ":server_config" ] diff --git a/src/app/server/DefaultEnhancedSetupFlowProvider.cpp b/src/app/server/DefaultEnhancedSetupFlowProvider.cpp new file mode 100644 index 00000000000000..750e21fafc31c2 --- /dev/null +++ b/src/app/server/DefaultEnhancedSetupFlowProvider.cpp @@ -0,0 +1,151 @@ +/* + * + * Copyright (c) 2024 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 "DefaultEnhancedSetupFlowProvider.h" + +#include +#include +#include + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::Init(TermsAndConditionsProvider * const inTermsAndConditionsProvider) +{ + VerifyOrReturnError(nullptr != inTermsAndConditionsProvider, CHIP_ERROR_INVALID_ARGUMENT); + + mTermsAndConditionsProvider = inTermsAndConditionsProvider; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +chip::app::DefaultEnhancedSetupFlowProvider::HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(bool & outAccepted) const +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion)); + + if (0 == requiredAcknowledgements) + { + outAccepted = true; + return CHIP_NO_ERROR; + } + + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion)); + + outAccepted = ((requiredAcknowledgements & acceptedAcknowledgements) == requiredAcknowledgements); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted( + bool & outAccepted) const +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion)); + + if (0 == requiredAcknowledgementsVersion) + { + outAccepted = true; + return CHIP_NO_ERROR; + } + + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion)); + + outAccepted = (acceptedAcknowledgementsVersion >= requiredAcknowledgementsVersion); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::GetTermsAndConditionsRequiredAcknowledgements(uint16_t & outValue) const +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion)); + + outValue = requiredAcknowledgements; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +chip::app::DefaultEnhancedSetupFlowProvider::GetTermsAndConditionsRequiredAcknowledgementsVersion(uint16_t & outValue) const +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion)); + + outValue = requiredAcknowledgementsVersion; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgements(uint16_t & outValue) const +{ + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion)); + + outValue = acceptedAcknowledgements; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +chip::app::DefaultEnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgementsVersion(uint16_t & outValue) const +{ + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion)); + + outValue = acceptedAcknowledgementsVersion; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::SetTermsAndConditionsAcceptance(uint16_t inTCAcknowledgementsValue, + uint16_t inTCAcknowledgementsVersionValue) +{ + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->SetAcceptance(inTCAcknowledgementsValue, inTCAcknowledgementsVersionValue)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::ClearTermsAndConditionsAcceptance() +{ + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->ClearAcceptance()); + + return CHIP_NO_ERROR; +} diff --git a/src/app/server/DefaultEnhancedSetupFlowProvider.h b/src/app/server/DefaultEnhancedSetupFlowProvider.h new file mode 100644 index 00000000000000..eb9d145d6221d4 --- /dev/null +++ b/src/app/server/DefaultEnhancedSetupFlowProvider.h @@ -0,0 +1,87 @@ +/* + * + * Copyright (c) 2024 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. + */ + +#pragma once + +#include "EnhancedSetupFlowProvider.h" + +#include + +#include + +#include "TermsAndConditionsProvider.h" + +namespace chip { +namespace app { +class DefaultEnhancedSetupFlowProvider : public EnhancedSetupFlowProvider +{ +public: + /** + * @brief Initializes the EnhancedSetupFlowProvider. + * + * @param[in] inTermsAndConditionsProvider The terms and conditions provide dependency. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + CHIP_ERROR Init(TermsAndConditionsProvider * const inTermsAndConditionsProvider); + + /** + * @copydoc EnhancedSetupFlowProvider::HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted + */ + CHIP_ERROR HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(bool & outAccepted) const override; + + /** + * @copydoc EnhancedSetupFlowProvider::HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted + */ + CHIP_ERROR HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(bool & outAccepted) const override; + + /** + * @copydoc EnhancedSetupFlowProvider::GetTermsAndConditionsRequiredAcknowledgements + */ + CHIP_ERROR GetTermsAndConditionsRequiredAcknowledgements(uint16_t & outValue) const override; + + /** + * @copydoc EnhancedSetupFlowProvider::GetTermsAndConditionsRequiredAcknowledgementsVersion + */ + CHIP_ERROR GetTermsAndConditionsRequiredAcknowledgementsVersion(uint16_t & outValue) const override; + + /** + * @copydoc EnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgements + */ + CHIP_ERROR GetTermsAndConditionsAcceptedAcknowledgements(uint16_t & outValue) const override; + + /** + * @copydoc EnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgementsVersion + */ + CHIP_ERROR GetTermsAndConditionsAcceptedAcknowledgementsVersion(uint16_t & outValue) const override; + + /** + * @copydoc EnhancedSetupFlowProvider::SetTermsAndConditionsAcceptance + */ + CHIP_ERROR SetTermsAndConditionsAcceptance(uint16_t aTCAcknowledgements, uint16_t aTCAcknowledgementsVersion) override; + + /** + * @copydoc EnhancedSetupFlowProvider::ClearTermsAndConditionsAcceptance + */ + CHIP_ERROR ClearTermsAndConditionsAcceptance() override; + +private: + TermsAndConditionsProvider * mTermsAndConditionsProvider; /**< TermsAndConditionsProvider instance. */ +}; + +}; // namespace app +}; // namespace chip diff --git a/src/app/server/DefaultTermsAndConditionsProvider.cpp b/src/app/server/DefaultTermsAndConditionsProvider.cpp new file mode 100644 index 00000000000000..564c80b16e783d --- /dev/null +++ b/src/app/server/DefaultTermsAndConditionsProvider.cpp @@ -0,0 +1,129 @@ +/* + * + * Copyright (c) 2024 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 "DefaultTermsAndConditionsProvider.h" + +#include +#include +#include +#include +#include +#include + +namespace { +static constexpr chip::TLV::Tag kAcceptedAcknowledgementsTag = chip::TLV::ContextTag(1); +static constexpr chip::TLV::Tag kAcceptedAcknowledgementsVersionTag = chip::TLV::ContextTag(2); +static constexpr size_t kEstimatedTlvBufferSize = chip::TLV::EstimateStructOverhead(sizeof(uint16_t), sizeof(uint16_t)); +}; // namespace + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::Init(chip::PersistentStorageDelegate * const inPersistentStorageDelegate, + uint16_t inRequiredAcknowledgementsValue, + uint16_t inRequiredAcknowledgementsVersionValue) +{ + VerifyOrReturnError(nullptr != inPersistentStorageDelegate, CHIP_ERROR_INVALID_ARGUMENT); + + mPersistentStorageDelegate = inPersistentStorageDelegate; + mRequiredAcknowledgementsValue = inRequiredAcknowledgementsValue; + mRequiredAcknowledgementsVersionValue = inRequiredAcknowledgementsVersionValue; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::ClearAcceptance() +{ + VerifyOrReturnError(nullptr != mPersistentStorageDelegate, CHIP_ERROR_UNINITIALIZED); + + const chip::StorageKeyName storageKey = DefaultStorageKeyAllocator::TermsAndConditionsAcceptance(); + ReturnErrorOnFailure(mPersistentStorageDelegate->SyncDeleteKeyValue(storageKey.KeyName())); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::GetAcceptance(uint16_t & outAcknowledgementsValue, + uint16_t & outAcknowledgementsVersionValue) const +{ + uint16_t acknowledgements = 0; + uint16_t acknowledgementsVersion = 0; + + chip::TLV::TLVReader tlvReader; + chip::TLV::TLVType tlvContainer; + + uint8_t buffer[kEstimatedTlvBufferSize] = { 0 }; + uint16_t bufferSize = sizeof(buffer); + + VerifyOrReturnError(nullptr != mPersistentStorageDelegate, CHIP_ERROR_UNINITIALIZED); + + const chip::StorageKeyName storageKey = DefaultStorageKeyAllocator::TermsAndConditionsAcceptance(); + CHIP_ERROR err = mPersistentStorageDelegate->SyncGetKeyValue(storageKey.KeyName(), &buffer, bufferSize); + if (CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == err) + { + outAcknowledgementsValue = acknowledgements; + outAcknowledgementsVersionValue = acknowledgementsVersion; + + return CHIP_NO_ERROR; + } + + VerifyOrReturnError(CHIP_NO_ERROR == err, err); + + tlvReader.Init(buffer); + ReturnErrorOnFailure(tlvReader.Next(chip::TLV::kTLVType_Structure, chip::TLV::AnonymousTag())); + ReturnErrorOnFailure(tlvReader.EnterContainer(tlvContainer)); + ReturnErrorOnFailure(tlvReader.Next()); + ReturnErrorOnFailure(tlvReader.Expect(kAcceptedAcknowledgementsTag)); + ReturnErrorOnFailure(tlvReader.Get(acknowledgements)); + ReturnErrorOnFailure(tlvReader.Next()); + ReturnErrorOnFailure(tlvReader.Expect(kAcceptedAcknowledgementsVersionTag)); + ReturnErrorOnFailure(tlvReader.Get(acknowledgementsVersion)); + ReturnErrorOnFailure(tlvReader.ExitContainer(tlvContainer)); + + outAcknowledgementsValue = acknowledgements; + outAcknowledgementsVersionValue = acknowledgementsVersion; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::GetRequirements(uint16_t & outAcknowledgementsValue, + uint16_t & outAcknowledgementsVersionValue) const +{ + outAcknowledgementsValue = mRequiredAcknowledgementsValue; + outAcknowledgementsVersionValue = mRequiredAcknowledgementsVersionValue; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::SetAcceptance(uint16_t inAcceptedAcknowledgementsValue, + uint16_t inAcceptedAcknowledgementsVersionValue) +{ + uint8_t buffer[kEstimatedTlvBufferSize] = { 0 }; + chip::TLV::TLVWriter tlvWriter; + chip::TLV::TLVType tlvContainer; + + VerifyOrReturnError(nullptr != mPersistentStorageDelegate, CHIP_ERROR_UNINITIALIZED); + + tlvWriter.Init(buffer, sizeof(buffer)); + ReturnErrorOnFailure(tlvWriter.StartContainer(chip::TLV::AnonymousTag(), chip::TLV::kTLVType_Structure, tlvContainer)); + ReturnErrorOnFailure(tlvWriter.Put(kAcceptedAcknowledgementsTag, inAcceptedAcknowledgementsValue)); + ReturnErrorOnFailure(tlvWriter.Put(kAcceptedAcknowledgementsVersionTag, inAcceptedAcknowledgementsVersionValue)); + ReturnErrorOnFailure(tlvWriter.EndContainer(tlvContainer)); + ReturnErrorOnFailure(tlvWriter.Finalize()); + + const chip::StorageKeyName storageKey = DefaultStorageKeyAllocator::TermsAndConditionsAcceptance(); + ReturnErrorOnFailure(mPersistentStorageDelegate->SyncSetKeyValue(storageKey.KeyName(), buffer, sizeof(buffer))); + + return CHIP_NO_ERROR; +} diff --git a/src/app/server/DefaultTermsAndConditionsProvider.h b/src/app/server/DefaultTermsAndConditionsProvider.h new file mode 100644 index 00000000000000..a6a47f22f4a5dd --- /dev/null +++ b/src/app/server/DefaultTermsAndConditionsProvider.h @@ -0,0 +1,71 @@ +/* + * + * Copyright (c) 2024 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. + */ + +#pragma once + +#include "TermsAndConditionsProvider.h" + +#include + +#include +#include + +namespace chip { +namespace app { +class DefaultTermsAndConditionsProvider : public TermsAndConditionsProvider +{ +public: + /** + * @brief Initializes the TermsAndConditionsProvider. + * + * @param[in] inPersistentStorageDelegate Persistent storage delegate dependency. + * @param[in] inRequiredAcknowledgementsValue The bitmask of required acknowledgements. + * @param[in] inRequiredAcknowledgementsVersionValue The version of the required acknowledgements. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + CHIP_ERROR Init(chip::PersistentStorageDelegate * const inPersistentStorageDelegate, uint16_t inRequiredAcknowledgementsValue, + uint16_t inRequiredAcknowledgementsVersionValue); + + /** + * @copydoc TermsAndConditionsProvider::ClearAcceptance + */ + CHIP_ERROR ClearAcceptance() override; + + /** + * @copydoc TermsAndConditionsProvider::GetAcceptance + */ + CHIP_ERROR GetAcceptance(uint16_t & outAcknowledgementsValue, uint16_t & outAcknowledgementsVersionValue) const override; + + /** + * @copydoc TermsAndConditionsProvider::GetRequirements + */ + CHIP_ERROR GetRequirements(uint16_t & outAcknowledgementsValue, uint16_t & outAcknowledgementsVersionValue) const override; + + /** + * @copydoc TermsAndConditionsProvider::SetAcceptance + */ + CHIP_ERROR SetAcceptance(uint16_t inAcknowledgementsValue, uint16_t inAcknowledgementsVersionValue) override; + +private: + chip::PersistentStorageDelegate * mPersistentStorageDelegate; + uint16_t mRequiredAcknowledgementsValue; + uint16_t mRequiredAcknowledgementsVersionValue; +}; + +}; // namespace app +}; // namespace chip diff --git a/src/app/server/EnhancedSetupFlowProvider.h b/src/app/server/EnhancedSetupFlowProvider.h new file mode 100644 index 00000000000000..6db950b87e7fa5 --- /dev/null +++ b/src/app/server/EnhancedSetupFlowProvider.h @@ -0,0 +1,108 @@ +/* + * + * Copyright (c) 2024 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. + */ + +#pragma once + +#include + +#include + +namespace chip { +namespace app { + +/** + * @brief Feature state access layer for the EnhancedSetupFlowProvider. + * + * This class provides access to the state of the feature through the TermsAndConditionsProvider. + */ +class EnhancedSetupFlowProvider +{ +public: + /** + * @brief Destructor. + */ + virtual ~EnhancedSetupFlowProvider() = default; + + /** + * @brief Checks if the required terms and conditions acknowledgements have been accepted. + * + * @param[out] outAccepted true if the required acknowledgements have been accepted, false otherwise. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(bool & outAccepted) const = 0; + + /** + * @brief Checks if the required terms and conditions acknowledgements version has been accepted. + * + * @param[out] outAccepted true if the required acknowledgements version has been accepted, false otherwise. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(bool & outAccepted) const = 0; + + /** + * @brief Retrieves the required terms and conditions acknowledgements. + * + * @param[out] outValue The version of the required acknowledgements. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR GetTermsAndConditionsRequiredAcknowledgements(uint16_t & outValue) const = 0; + + /** + * @brief Retrieves the required terms and conditions acknowledgements version. + * + * @param[out] outValue The outValue of the required acknowledgements version. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR GetTermsAndConditionsRequiredAcknowledgementsVersion(uint16_t & outValue) const = 0; + + /** + * @brief Retrieves the accepted terms and conditions acknowledgements. + * + * @param[out] outValue The outValue of the accepted acknowledgements. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR GetTermsAndConditionsAcceptedAcknowledgements(uint16_t & outValue) const = 0; + + /** + * @brief Retrieves the accepted terms and conditions acknowledgements version. + * + * @param[out] outValue The outValue of the accepted acknowledgements version. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR GetTermsAndConditionsAcceptedAcknowledgementsVersion(uint16_t & outValue) const = 0; + + /** + * @brief Sets the acceptance status of the terms and conditions. + * + * @param[in] inTCAcknowledgements The acknowledgements to accept. + * @param[in] inTCAcknowledgementsoutValue The version of the acknowledgements to accept. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR SetTermsAndConditionsAcceptance(uint16_t inTCAcknowledgementsValue, + uint16_t inTCAcknowledgementsoutValue) = 0; + + /** + * @brief Clears the acceptance status of the terms and conditions. + * + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR ClearTermsAndConditionsAcceptance() = 0; +}; + +}; // namespace app +}; // namespace chip diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index a71d797651b418..cc981c4939bc96 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2024 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. @@ -21,6 +21,11 @@ #include #include +#include +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION +#include +#include +#endif #include #include #include @@ -101,6 +106,103 @@ static ::chip::PersistedCounter sGlobalEventIdCounter; static ::chip::app::CircularEventBuffer sLoggingBuffer[CHIP_NUM_EVENT_LOGGING_BUFFERS]; #endif // CHIP_CONFIG_ENABLE_SERVER_IM_EVENT +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION +app::DefaultEnhancedSetupFlowProvider sDefaultEnhancedSetupFlowProviderInstance; +app::EnhancedSetupFlowProvider * CommonCaseDeviceServerInitParams::sDefaultEnhancedSetupFlowProvider = + &sDefaultEnhancedSetupFlowProviderInstance; + +app::DefaultTermsAndConditionsProvider sDefaultTermsAndConditionsProviderInstance; +app::TermsAndConditionsProvider * CommonCaseDeviceServerInitParams::sDefaultTermsAndConditionsProvider = + &sDefaultTermsAndConditionsProviderInstance; +#endif + +CHIP_ERROR CommonCaseDeviceServerInitParams::InitializeStaticResourcesBeforeServerInit() +{ + chip::DeviceLayer::PersistedStorage::KeyValueStoreManager & kvsManager = DeviceLayer::PersistedStorage::KeyValueStoreMgr(); + + // KVS-based persistent storage delegate injection + if (persistentStorageDelegate == nullptr) + { + ReturnErrorOnFailure(sKvsPersistenStorageDelegate.Init(&kvsManager)); + this->persistentStorageDelegate = &sKvsPersistenStorageDelegate; + } + + // PersistentStorageDelegate "software-based" operational key access injection + if (this->operationalKeystore == nullptr) + { + // WARNING: PersistentStorageOperationalKeystore::Finish() is never called. It's fine for + // for examples and for now. + ReturnErrorOnFailure(sPersistentStorageOperationalKeystore.Init(this->persistentStorageDelegate)); + this->operationalKeystore = &sPersistentStorageOperationalKeystore; + } + + // OpCertStore can be injected but default to persistent storage default + // for simplicity of the examples. + if (this->opCertStore == nullptr) + { + // WARNING: PersistentStorageOpCertStore::Finish() is never called. It's fine for + // for examples and for now, since all storage is immediate for that impl. + ReturnErrorOnFailure(sPersistentStorageOpCertStore.Init(this->persistentStorageDelegate)); + this->opCertStore = &sPersistentStorageOpCertStore; + } + + // Injection of report scheduler WILL lead to two schedulers being allocated. As recommended above, this should only be used + // for IN-TREE examples. If a default scheduler is desired, the basic ServerInitParams should be used by the application and + // CommonCaseDeviceServerInitParams should not be allocated. + if (this->reportScheduler == nullptr) + { + reportScheduler = &sReportScheduler; + } + +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + if (this->termsAndConditionsProvider == nullptr) + { + ReturnErrorOnFailure(sDefaultTermsAndConditionsProviderInstance.Init(this->persistentStorageDelegate, + CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS, + CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION)); + this->termsAndConditionsProvider = sDefaultTermsAndConditionsProvider; + } + + if (this->enhancedSetupFlowProvider == nullptr) + { + ReturnErrorOnFailure(sDefaultEnhancedSetupFlowProviderInstance.Init(this->termsAndConditionsProvider)); + this->enhancedSetupFlowProvider = sDefaultEnhancedSetupFlowProvider; + } +#endif + + // Session Keystore injection + this->sessionKeystore = &sSessionKeystore; + + // Group Data provider injection + sGroupDataProvider.SetStorageDelegate(this->persistentStorageDelegate); + sGroupDataProvider.SetSessionKeystore(this->sessionKeystore); + ReturnErrorOnFailure(sGroupDataProvider.Init()); + this->groupDataProvider = &sGroupDataProvider; + +#if CHIP_CONFIG_ENABLE_SESSION_RESUMPTION + ReturnErrorOnFailure(sSessionResumptionStorage.Init(this->persistentStorageDelegate)); + this->sessionResumptionStorage = &sSessionResumptionStorage; +#else + this->sessionResumptionStorage = nullptr; +#endif + + // Inject access control delegate + this->accessDelegate = Access::Examples::GetAccessControlDelegate(); + + // Inject ACL storage. (Don't initialize it.) + this->aclStorage = &sAclStorage; + +#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS + ChipLogProgress(AppServer, "Initializing subscription resumption storage..."); + ReturnErrorOnFailure(sSubscriptionResumptionStorage.Init(this->persistentStorageDelegate)); + this->subscriptionResumptionStorage = &sSubscriptionResumptionStorage; +#else + ChipLogProgress(AppServer, "Subscription persistence not supported"); +#endif + + return CHIP_NO_ERROR; +} + CHIP_ERROR Server::Init(const ServerInitParams & initParams) { ChipLogProgress(AppServer, "Server initializing..."); @@ -125,6 +227,10 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) VerifyOrExit(initParams.operationalKeystore != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(initParams.opCertStore != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(initParams.reportScheduler != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + VerifyOrExit(initParams.enhancedSetupFlowProvider != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(initParams.termsAndConditionsProvider != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); +#endif // TODO(16969): Remove chip::Platform::MemoryInit() call from Server class, it belongs to outer code chip::Platform::MemoryInit(); @@ -181,6 +287,11 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) mReportScheduler = initParams.reportScheduler; +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + mTermsAndConditionsProvider = initParams.termsAndConditionsProvider; + mEnhancedSetupFlowProvider = initParams.enhancedSetupFlowProvider; +#endif + mTestEventTriggerDelegate = initParams.testEventTriggerDelegate; deviceInfoprovider = DeviceLayer::GetDeviceInfoProvider(); diff --git a/src/app/server/Server.h b/src/app/server/Server.h index d649e0fc923896..f3d65f9983ec0d 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2024 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. @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -67,6 +69,7 @@ #endif #include #include +#include #include #if CHIP_CONFIG_ENABLE_ICD_SERVER @@ -164,6 +167,12 @@ struct ServerInitParams Credentials::OperationalCertificateStore * opCertStore = nullptr; // Required, if not provided, the Server::Init() WILL fail. app::reporting::ReportScheduler * reportScheduler = nullptr; +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + // Optional. Enhanced setup flow provider to support terms and conditions acceptance check. + app::EnhancedSetupFlowProvider * enhancedSetupFlowProvider = nullptr; + // Optional. Terms and conditions provider to support enhanced setup flow feature. + app::TermsAndConditionsProvider * termsAndConditionsProvider = nullptr; +#endif }; /** @@ -210,76 +219,7 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams * @return CHIP_NO_ERROR on success or a CHIP_ERROR value from APIs called to initialize * resources on failure. */ - CHIP_ERROR InitializeStaticResourcesBeforeServerInit() - { - // KVS-based persistent storage delegate injection - if (persistentStorageDelegate == nullptr) - { - chip::DeviceLayer::PersistedStorage::KeyValueStoreManager & kvsManager = - DeviceLayer::PersistedStorage::KeyValueStoreMgr(); - ReturnErrorOnFailure(sKvsPersistenStorageDelegate.Init(&kvsManager)); - this->persistentStorageDelegate = &sKvsPersistenStorageDelegate; - } - - // PersistentStorageDelegate "software-based" operational key access injection - if (this->operationalKeystore == nullptr) - { - // WARNING: PersistentStorageOperationalKeystore::Finish() is never called. It's fine for - // for examples and for now. - ReturnErrorOnFailure(sPersistentStorageOperationalKeystore.Init(this->persistentStorageDelegate)); - this->operationalKeystore = &sPersistentStorageOperationalKeystore; - } - - // OpCertStore can be injected but default to persistent storage default - // for simplicity of the examples. - if (this->opCertStore == nullptr) - { - // WARNING: PersistentStorageOpCertStore::Finish() is never called. It's fine for - // for examples and for now, since all storage is immediate for that impl. - ReturnErrorOnFailure(sPersistentStorageOpCertStore.Init(this->persistentStorageDelegate)); - this->opCertStore = &sPersistentStorageOpCertStore; - } - - // Injection of report scheduler WILL lead to two schedulers being allocated. As recommended above, this should only be used - // for IN-TREE examples. If a default scheduler is desired, the basic ServerInitParams should be used by the application and - // CommonCaseDeviceServerInitParams should not be allocated. - if (this->reportScheduler == nullptr) - { - reportScheduler = &sReportScheduler; - } - - // Session Keystore injection - this->sessionKeystore = &sSessionKeystore; - - // Group Data provider injection - sGroupDataProvider.SetStorageDelegate(this->persistentStorageDelegate); - sGroupDataProvider.SetSessionKeystore(this->sessionKeystore); - ReturnErrorOnFailure(sGroupDataProvider.Init()); - this->groupDataProvider = &sGroupDataProvider; - -#if CHIP_CONFIG_ENABLE_SESSION_RESUMPTION - ReturnErrorOnFailure(sSessionResumptionStorage.Init(this->persistentStorageDelegate)); - this->sessionResumptionStorage = &sSessionResumptionStorage; -#else - this->sessionResumptionStorage = nullptr; -#endif - - // Inject access control delegate - this->accessDelegate = Access::Examples::GetAccessControlDelegate(); - - // Inject ACL storage. (Don't initialize it.) - this->aclStorage = &sAclStorage; - -#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS - ChipLogProgress(AppServer, "Initializing subscription resumption storage..."); - ReturnErrorOnFailure(sSubscriptionResumptionStorage.Init(this->persistentStorageDelegate)); - this->subscriptionResumptionStorage = &sSubscriptionResumptionStorage; -#else - ChipLogProgress(AppServer, "Subscription persistence not supported"); -#endif - - return CHIP_NO_ERROR; - } + CHIP_ERROR InitializeStaticResourcesBeforeServerInit(); private: static KvsPersistentStorageDelegate sKvsPersistenStorageDelegate; @@ -297,6 +237,10 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams #endif static app::DefaultAclStorage sAclStorage; static Crypto::DefaultSessionKeystore sSessionKeystore; +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + static app::EnhancedSetupFlowProvider * sDefaultEnhancedSetupFlowProvider; + static app::TermsAndConditionsProvider * sDefaultTermsAndConditionsProvider; +#endif }; /** @@ -377,6 +321,10 @@ class Server app::reporting::ReportScheduler * GetReportScheduler() { return mReportScheduler; } +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + app::EnhancedSetupFlowProvider * GetEnhancedSetupFlowProvider() { return mEnhancedSetupFlowProvider; } +#endif + #if CHIP_CONFIG_ENABLE_ICD_SERVER app::ICDManager & GetICDManager() { return mICDManager; } @@ -650,6 +598,10 @@ class Server GroupDataProviderListener mListener; ServerFabricDelegate mFabricDelegate; app::reporting::ReportScheduler * mReportScheduler; +#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + app::EnhancedSetupFlowProvider * mEnhancedSetupFlowProvider; + app::TermsAndConditionsProvider * mTermsAndConditionsProvider; +#endif Access::AccessControl mAccessControl; app::AclStorage * mAclStorage; diff --git a/src/app/server/TermsAndConditionsProvider.h b/src/app/server/TermsAndConditionsProvider.h new file mode 100644 index 00000000000000..79b6da751acbb7 --- /dev/null +++ b/src/app/server/TermsAndConditionsProvider.h @@ -0,0 +1,75 @@ +/* + * + * Copyright (c) 2024 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. + */ + +#pragma once + +#include + +#include + +namespace chip { +namespace app { + +/** + * @brief Data access layer for the required terms and conditions and the store for the user acceptance. + */ +class TermsAndConditionsProvider +{ +public: + /** + * @brief Destructor. + */ + virtual ~TermsAndConditionsProvider() = default; + + /** + * @brief Sets the acceptance status of the required terms and conditions. + * + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR ClearAcceptance() = 0; + + /** + * @brief Retrieves the acceptance status of the required terms and conditions. + * + * @param[out] outAcknowledgementsValue The bitmask of acknowledgements accepted. + * @param[out] outAcknowledgementsVersionValue The version of the accepted acknowledgements. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR GetAcceptance(uint16_t & outAcknowledgementsValue, uint16_t & outAcknowledgementsVersionValue) const = 0; + + /** + * @brief Retrieves the requirements of the terms and conditions. + * + * @param[out] outAcknowledgementsValue The bitmask of required acknowledgements. + * @param[out] outAcknowledgementsVersionValue The version of the required acknowledgements. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR GetRequirements(uint16_t & outAcknowledgementsValue, uint16_t & outAcknowledgementsVersionValue) const = 0; + + /** + * @brief Sets the acceptance status of the required terms and conditions. + * + * @param[in] inAcknowledgementsValue The bitmask of acknowledgements that was accepted. + * @param[in] inAcknowledgementsVersionValue The version of the acknowledgements that was accepted. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR SetAcceptance(uint16_t inAcknowledgementsValue, uint16_t inAcknowledgementsVersionValue) = 0; +}; + +}; // namespace app +}; // namespace chip diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index e8860f01565141..7ae7f73218459f 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2020 Project CHIP Authors +# Copyright (c) 2020-2024 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. @@ -155,7 +155,9 @@ chip_test_suite("tests") { "TestCommandPathParams.cpp", "TestConcreteAttributePath.cpp", "TestDataModelSerialization.cpp", + "TestDefaultEnhancedSetupFlowProvider.cpp", "TestDefaultOTARequestorStorage.cpp", + "TestDefaultTermsAndConditionsProvider.cpp", "TestEventLoggingNoUTCTime.cpp", "TestEventOverflow.cpp", "TestEventPathParams.cpp", diff --git a/src/app/tests/TestDefaultEnhancedSetupFlowProvider.cpp b/src/app/tests/TestDefaultEnhancedSetupFlowProvider.cpp new file mode 100644 index 00000000000000..e75418c9b59c32 --- /dev/null +++ b/src/app/tests/TestDefaultEnhancedSetupFlowProvider.cpp @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2024 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 "app/server/DefaultEnhancedSetupFlowProvider.h" + +#include +#include + +class FakeTermsAndConditionsProvider : public chip::app::TermsAndConditionsProvider +{ +public: + FakeTermsAndConditionsProvider(uint16_t inAcceptedAcknowledgements, uint16_t inAcceptedAcknowledgementsVersion, + uint16_t inRequiredAcknowledgements, uint16_t inRequiredAcknowledgementsVersion) : + mAcceptedAcknowledgements(inAcceptedAcknowledgements), + mAcceptedAcknowledgementsVersion(inAcceptedAcknowledgementsVersion), mRequiredAcknowledgements(inRequiredAcknowledgements), + mRequiredAcknowledgementsVersion(inRequiredAcknowledgementsVersion) + {} + + CHIP_ERROR GetAcceptance(uint16_t & outAcknowledgements, uint16_t & outAcknowledgementsVersion) const override + { + outAcknowledgements = mAcceptedAcknowledgements; + outAcknowledgementsVersion = mAcceptedAcknowledgementsVersion; + return CHIP_NO_ERROR; + } + + CHIP_ERROR GetRequirements(uint16_t & outAcknowledgements, uint16_t & outAcknowledgementsVersion) const override + { + outAcknowledgements = mRequiredAcknowledgements; + outAcknowledgementsVersion = mRequiredAcknowledgementsVersion; + return CHIP_NO_ERROR; + } + + CHIP_ERROR SetAcceptance(uint16_t inAcknowledgements, uint16_t inAcknowledgementsVersion) override + { + mAcceptedAcknowledgements = inAcknowledgements; + mAcceptedAcknowledgementsVersion = inAcknowledgementsVersion; + return CHIP_NO_ERROR; + } + + CHIP_ERROR ClearAcceptance() override + { + mAcceptedAcknowledgements = 0; + mAcceptedAcknowledgementsVersion = 0; + return CHIP_NO_ERROR; + } + +private: + uint16_t mAcceptedAcknowledgements; + uint16_t mAcceptedAcknowledgementsVersion; + uint16_t mRequiredAcknowledgements; + uint16_t mRequiredAcknowledgementsVersion; +}; + +TEST(DefaultEnhancedSetupFlowProvider, TestNoAcceptanceRequiredCheckAcknowledgementsAcceptedSuccess) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + + FakeTermsAndConditionsProvider tncProvider(0, 0, 0, 0); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(hasTermsBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestNoAcceptanceRequiredCheckAcknowledgementsVersionAcceptedSuccess) +{ + CHIP_ERROR err; + bool hasTermsVersionBeenAccepted; + + FakeTermsAndConditionsProvider tncProvider(0, 0, 0, 0); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestAcceptanceRequiredNoTermsAcceptedCheckAcknowledgementsAcceptedFailure) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + + FakeTermsAndConditionsProvider tncProvider(0, 0, 1, 1); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(!hasTermsBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, + TestAcceptanceRequiredTermsAcceptedTermsVersionOutdatedCheckAcknowledgementsVersionAcceptedFailure) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + bool hasTermsVersionBeenAccepted; + + FakeTermsAndConditionsProvider tncProvider(0, 0, 1, 1); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.SetTermsAndConditionsAcceptance(1, 0); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(hasTermsBeenAccepted); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(!hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestAcceptanceRequiredTermsAcceptedFutureVersionCheckAcknowledgementsAcceptedSuccess) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + bool hasTermsVersionBeenAccepted; + + uint16_t acceptedTerms = 1; + uint16_t requiredTerms = 1; + uint16_t acceptedTermsVersion = 2; + uint16_t requiredTermsVersion = 1; + + FakeTermsAndConditionsProvider tncProvider(acceptedTerms, acceptedTermsVersion, requiredTerms, requiredTermsVersion); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(hasTermsBeenAccepted); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestAcceptanceRequiredTermsAcceptedSuccess) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + bool hasTermsVersionBeenAccepted; + + FakeTermsAndConditionsProvider tncProvider(0, 0, 1, 1); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.SetTermsAndConditionsAcceptance(1, 1); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(hasTermsBeenAccepted); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestAcceptanceRequiredTermsMissingFailure) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + bool hasTermsVersionBeenAccepted; + + uint16_t acceptedTerms = 0b0111'1111'1111'1111; + uint16_t requiredTerms = 0b1111'1111'1111'1111; + uint16_t acceptedTermsVersion = 1; + uint16_t requiredTermsVersion = 1; + + FakeTermsAndConditionsProvider tncProvider(acceptedTerms, acceptedTermsVersion, requiredTerms, requiredTermsVersion); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(!hasTermsBeenAccepted); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestAcceptanceRequiredAllTermsAcceptedCheckAcknowledgementsAcceptedSuccess) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + bool hasTermsVersionBeenAccepted; + + uint16_t acceptedTerms = 0b1111'1111'1111'1111; + uint16_t requiredTerms = 0b1111'1111'1111'1111; + uint16_t acceptedTermsVersion = 1; + uint16_t requiredTermsVersion = 1; + + FakeTermsAndConditionsProvider tncProvider(acceptedTerms, acceptedTermsVersion, requiredTerms, requiredTermsVersion); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(hasTermsBeenAccepted); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestClearAcceptanceRetainsRequirements) +{ + CHIP_ERROR err; + + uint16_t initialAcceptedTermsAndConditions = 0; + uint16_t initialRequiredTermsAndConditions = 0b1111'1111'1111'1111; + uint16_t initialAcceptedTermsAndConditionsVersion = 0; + uint16_t initialRequiredTermsAndConditionsVersion = 1; + + uint16_t outAcceptedTermsAndConditions; + uint16_t outRequiredTermsAndConditions; + uint16_t outAcceptedTermsAndConditionsVersion; + uint16_t outRequiredTermsAndConditionsVersion; + + uint16_t updatedAcceptedTermsAndConditions = 0b1111'1111'1111'1111; + uint16_t updatedAcceptedTermsAndConditionsVersion = 1; + + FakeTermsAndConditionsProvider tncProvider(initialAcceptedTermsAndConditions, initialAcceptedTermsAndConditionsVersion, + initialRequiredTermsAndConditions, initialRequiredTermsAndConditionsVersion); + + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.SetTermsAndConditionsAcceptance(updatedAcceptedTermsAndConditions, updatedAcceptedTermsAndConditionsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.GetTermsAndConditionsRequiredAcknowledgements(outRequiredTermsAndConditions); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(outRequiredTermsAndConditions == initialRequiredTermsAndConditions); + + err = esfProvider.GetTermsAndConditionsRequiredAcknowledgementsVersion(outRequiredTermsAndConditionsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(outRequiredTermsAndConditionsVersion == initialRequiredTermsAndConditionsVersion); + + err = esfProvider.GetTermsAndConditionsAcceptedAcknowledgements(outAcceptedTermsAndConditions); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(outAcceptedTermsAndConditions == updatedAcceptedTermsAndConditions); + + err = esfProvider.GetTermsAndConditionsAcceptedAcknowledgementsVersion(outAcceptedTermsAndConditionsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(outAcceptedTermsAndConditionsVersion == updatedAcceptedTermsAndConditionsVersion); + + err = esfProvider.ClearTermsAndConditionsAcceptance(); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = esfProvider.GetTermsAndConditionsRequiredAcknowledgements(outRequiredTermsAndConditions); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(outRequiredTermsAndConditions == initialRequiredTermsAndConditions); + + err = esfProvider.GetTermsAndConditionsRequiredAcknowledgementsVersion(outRequiredTermsAndConditionsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(outRequiredTermsAndConditionsVersion == initialRequiredTermsAndConditionsVersion); + + err = esfProvider.GetTermsAndConditionsAcceptedAcknowledgements(outAcceptedTermsAndConditions); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(outAcceptedTermsAndConditions == 0); + + err = esfProvider.GetTermsAndConditionsAcceptedAcknowledgementsVersion(outAcceptedTermsAndConditionsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(outAcceptedTermsAndConditionsVersion == 0); +} diff --git a/src/app/tests/TestDefaultTermsAndConditionsProvider.cpp b/src/app/tests/TestDefaultTermsAndConditionsProvider.cpp new file mode 100644 index 00000000000000..5b496998d895ea --- /dev/null +++ b/src/app/tests/TestDefaultTermsAndConditionsProvider.cpp @@ -0,0 +1,225 @@ +/* + * + * Copyright (c) 2024 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 "app/server/DefaultTermsAndConditionsProvider.h" + +#include +#include +#include + +TEST(DefaultTermsAndConditionsProvider, TestInitSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 1; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); +} + +TEST(DefaultTermsAndConditionsProvider, TestNoRequirementsGetRequirementsSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 0; + uint16_t requiredAcknowledgementsVersion = 0; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(0 == outAcceptance); + EXPECT_TRUE(0 == outAcknowledgementsVersion); +} + +TEST(DefaultTermsAndConditionsProvider, TestNeverAcceptanceGetAcceptanceSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 0b1111'1111'1111'1111; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(0 == outAcceptance); + EXPECT_TRUE(0 == outAcknowledgementsVersion); +} + +TEST(DefaultTermsAndConditionsProvider, TestTermsAcceptedPersistsSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + chip::app::DefaultTermsAndConditionsProvider anotherTncProvider; + + uint16_t requiredAcknowledgements = 1; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t acceptedTermsAndConditions = 1; + uint16_t acceptedTermsAndConditionsVersion = 1; + err = tncProvider.SetAcceptance(acceptedTermsAndConditions, acceptedTermsAndConditionsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(1 == outAcceptance); + EXPECT_TRUE(1 == outAcknowledgementsVersion); + + err = anotherTncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + err = anotherTncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(1 == outAcceptance); + EXPECT_TRUE(1 == outAcknowledgementsVersion); +} + +TEST(DefaultTermsAndConditionsProvider, TestTermsRequiredGetRequirementsSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t initialRequiredAcknowledgements = 1; + uint16_t initialRequiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, initialRequiredAcknowledgements, initialRequiredAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t outRequiredAcknowledgements; + uint16_t outRequiredAcknowledgementsVersion; + err = tncProvider.GetRequirements(outRequiredAcknowledgements, outRequiredAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(1 == outRequiredAcknowledgements); + EXPECT_TRUE(1 == outRequiredAcknowledgementsVersion); +} + +TEST(DefaultTermsAndConditionsProvider, TestSetAcceptanceGetAcceptanceSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 1; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t acceptedTermsAndConditions = 1; + uint16_t acceptedTermsAndConditionsVersion = 1; + err = tncProvider.SetAcceptance(acceptedTermsAndConditions, acceptedTermsAndConditionsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(1 == outAcceptance); + EXPECT_TRUE(1 == outAcknowledgementsVersion); +} + +TEST(DefaultTermsAndConditionsProvider, TestClearAcceptanceGetAcceptanceSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 1; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t acceptedTermsAndConditions = 1; + uint16_t acceptedTermsAndConditionsVersion = 1; + err = tncProvider.SetAcceptance(acceptedTermsAndConditions, acceptedTermsAndConditionsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(1 == outAcceptance); + EXPECT_TRUE(1 == outAcknowledgementsVersion); + + err = tncProvider.ClearAcceptance(); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t outAcceptance2; + uint16_t outAcknowledgementsVersion2; + err = tncProvider.GetAcceptance(outAcceptance2, outAcknowledgementsVersion2); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(0 == outAcceptance2); + EXPECT_TRUE(0 == outAcknowledgementsVersion2); +} + +TEST(DefaultTermsAndConditionsProvider, TestAcceptanceRequiredTermsMissingFailure) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 1; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t acceptedTermsAndConditions = 1; + uint16_t acceptedTermsAndConditionsVersion = 1; + err = tncProvider.SetAcceptance(acceptedTermsAndConditions, acceptedTermsAndConditionsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(1 == outAcceptance); + EXPECT_TRUE(1 == outAcknowledgementsVersion); + + err = tncProvider.ClearAcceptance(); + EXPECT_TRUE(CHIP_NO_ERROR == err); + + uint16_t outRequiredAcknowledgements; + uint16_t outRequiredAcknowledgementsVersion; + err = tncProvider.GetRequirements(outRequiredAcknowledgements, outRequiredAcknowledgementsVersion); + EXPECT_TRUE(CHIP_NO_ERROR == err); + EXPECT_TRUE(1 == outRequiredAcknowledgements); + EXPECT_TRUE(1 == outRequiredAcknowledgementsVersion); +} diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp index a72543853fa2d1..41368dc047e07d 100644 --- a/src/controller/AutoCommissioner.cpp +++ b/src/controller/AutoCommissioner.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -346,6 +346,8 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio case CommissioningStage::kArmFailsafe: return CommissioningStage::kConfigRegulatory; case CommissioningStage::kConfigRegulatory: + return CommissioningStage::kConfigureTCAcknowledgments; + case CommissioningStage::kConfigureTCAcknowledgments: if (mDeviceCommissioningInfo.requiresUTC) { return CommissioningStage::kConfigureUTCTime; diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 21cd71e2932b0a..11ec47e76942f4 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020-2022 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * Copyright (c) 2013-2017 Nest Labs, Inc. * All rights reserved. * @@ -2593,6 +2593,22 @@ void DeviceCommissioner::OnSetRegulatoryConfigResponse( commissioner->CommissioningStageComplete(err, report); } +void DeviceCommissioner::OnSetTCAcknowledgementsResponse( + void * context, const GeneralCommissioning::Commands::SetTCAcknowledgementsResponse::DecodableType & data) +{ + CommissioningDelegate::CommissioningReport report; + CHIP_ERROR err = CHIP_NO_ERROR; + + ChipLogProgress(Controller, "Received SetTCAcknowledgements response errorCode=%u", to_underlying(data.errorCode)); + if (data.errorCode != GeneralCommissioning::CommissioningErrorEnum::kOk) + { + err = CHIP_ERROR_INTERNAL; + report.Set(data.errorCode); + } + DeviceCommissioner * commissioner = static_cast(context); + commissioner->CommissioningStageComplete(err, report); +} + void DeviceCommissioner::OnSetTimeZoneResponse(void * context, const TimeSynchronization::Commands::SetTimeZoneResponse::DecodableType & data) { @@ -3051,6 +3067,28 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio } } break; + case CommissioningStage::kConfigureTCAcknowledgments: { + ChipLogProgress(Controller, "Setting Terms and Conditions"); + + if (!params.GetTermsAndConditionsAcknowledgement().HasValue()) + { + CommissioningStageComplete(CHIP_NO_ERROR); + } + + GeneralCommissioning::Commands::SetTCAcknowledgements::Type request; + TermsAndConditionsAcknowledgement termsAndConditionsAcknowledgement = params.GetTermsAndConditionsAcknowledgement().Value(); + request.TCUserResponse = termsAndConditionsAcknowledgement.acceptedTermsAndConditions; + request.TCVersion = termsAndConditionsAcknowledgement.acceptedTermsAndConditionsVersion; + CHIP_ERROR err = + SendCommissioningCommand(proxy, request, OnSetTCAcknowledgementsResponse, OnBasicFailure, endpoint, timeout); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Controller, "Failed to send SetTCAcknowledgements command: %" CHIP_ERROR_FORMAT, err.Format()); + CommissioningStageComplete(err); + return; + } + break; + } case CommissioningStage::kSendPAICertificateRequest: { ChipLogProgress(Controller, "Sending request for PAI certificate"); CHIP_ERROR err = SendCertificateChainRequestCommand(proxy, CertificateType::kPAI, timeout); diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index e08c6483710ed2..6b69a0bf329cdd 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020-2022 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * Copyright (c) 2013-2017 Nest Labs, Inc. * All rights reserved. * @@ -940,6 +940,9 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, static void OnSetRegulatoryConfigResponse( void * context, const chip::app::Clusters::GeneralCommissioning::Commands::SetRegulatoryConfigResponse::DecodableType & data); + static void OnSetTCAcknowledgementsResponse( + void * context, + const chip::app::Clusters::GeneralCommissioning::Commands::SetTCAcknowledgementsResponse::DecodableType & data); static void OnSetUTCError(void * context, CHIP_ERROR error); static void OnSetTimeZoneResponse(void * context, diff --git a/src/controller/CommissioningDelegate.h b/src/controller/CommissioningDelegate.h index 57d6a0c9a8da53..cff2895dfc5434 100644 --- a/src/controller/CommissioningDelegate.h +++ b/src/controller/CommissioningDelegate.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -40,6 +40,7 @@ enum CommissioningStage : uint8_t kReadCommissioningInfo2, ///< Query SupportsConcurrentConnection, ICD state, check for matching fabric kArmFailsafe, ///< Send ArmFailSafe (0x30:0) command to the device kConfigRegulatory, ///< Send SetRegulatoryConfig (0x30:2) command to the device + kConfigureTCAcknowledgments, ///< Send SetTCAcknowledgements (0x30:6) command to the device kConfigureUTCTime, ///< SetUTCTime if the DUT has a time cluster kConfigureTimeZone, ///< Configure a time zone if one is required and available kConfigureDSTOffset, ///< Configure DST offset if one is required and available @@ -101,6 +102,12 @@ struct WiFiCredentials WiFiCredentials(ByteSpan newSsid, ByteSpan newCreds) : ssid(newSsid), credentials(newCreds) {} }; +struct TermsAndConditionsAcknowledgement +{ + uint16_t acceptedTermsAndConditions; + uint16_t acceptedTermsAndConditionsVersion; +}; + struct NOCChainGenerationParameters { ByteSpan nocsrElements; @@ -165,6 +172,11 @@ class CommissioningParameters // The country code to be used for the node, if set. Optional GetCountryCode() const { return mCountryCode; } + Optional GetTermsAndConditionsAcknowledgement() const + { + return mTermsAndConditionsAcknowledgement; + } + // Time zone to set for the node // If required, this will be truncated to fit the max size allowable on the node Optional> GetTimeZone() const @@ -337,6 +349,13 @@ class CommissioningParameters return *this; } + CommissioningParameters & + SetTermsAndConditionsAcknowledgement(TermsAndConditionsAcknowledgement termsAndConditionsAcknowledgement) + { + mTermsAndConditionsAcknowledgement.SetValue(termsAndConditionsAcknowledgement); + return *this; + } + // The lifetime of the list buffer needs to exceed the lifetime of the CommissioningParameters object. CommissioningParameters & SetTimeZone(app::DataModel::List timeZone) @@ -601,6 +620,7 @@ class CommissioningParameters Optional mAttestationNonce; Optional mWiFiCreds; Optional mCountryCode; + Optional mTermsAndConditionsAcknowledgement; Optional mThreadOperationalDataset; Optional mNOCChainGenerationParameters; Optional mRootCert; diff --git a/src/lib/support/DefaultStorageKeyAllocator.h b/src/lib/support/DefaultStorageKeyAllocator.h index 04825c9ca4d719..c024e731364c59 100644 --- a/src/lib/support/DefaultStorageKeyAllocator.h +++ b/src/lib/support/DefaultStorageKeyAllocator.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2024 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. @@ -245,6 +245,10 @@ class DefaultStorageKeyAllocator // when new fabric is created, this list needs to be updated, // when client init DefaultICDClientStorage, this table needs to be loaded. static StorageKeyName ICDFabricList() { return StorageKeyName::FromConst("g/icdfl"); } + + // Terms and Conditions Acceptance Key + // Stores the terms and conditions acceptance including terms and conditions revision, TLV encoded + static StorageKeyName TermsAndConditionsAcceptance() { return StorageKeyName::FromConst("g/tc"); } }; } // namespace chip