From 84bf7583f068e8af310d2b59045ebbcd403c0f6f 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] feat: Integrate Terms and Conditions Acknowledgements in Commissioning Process 1. **Add support for setting Terms and Conditions acknowledgements** - Added functionality to set Terms and Conditions acknowledgements and acknowledgment version in the General Commissioning cluster. - Implemented corresponding commands and responses to handle acknowledgements. 2. **Enhance setup flow handling** - Refactored the setup flow handling to accommodate setting Terms and Conditions acknowledgements. - Updated the commissioning stages to include configuring Terms and Conditions acknowledgements. 3. **Handle setting Terms and Conditions acknowledgements** - Implemented setting Terms and Conditions acknowledgements in the commissioning process. - Implemented proper handling of command responses and error checking. 1. **Initial setup** ```bash mkdir -p $HOME/workspace git -C $HOME/workspace clone https://github.com/project-chip/zap.git --depth 1 git -C $HOME/workspace clone git@github.com:swan-amazon/connectedhomeip.git -b feature/enhanced-setup-flow --depth 1 git -C $HOME/workspace/connectedhomeip submodule update --init --recursive --depth 1 ``` 2. **Zap regen** ```bash ./scripts/tools/zap/zap_bootstrap.sh python3 ./scripts/tools/zap_regen_all.py python3 ./scripts/tools/zap_convert_all.py ``` 3. **Build the Application**: Configure and build the `chip-all-clusters-app` with Bluetooth and WiFi disabled to simulate an on-network only commissioning environment. ```bash gn gen --check --fail-on-unused-args --export-compile-commands --root=/workspace/connectedhomeip/examples/all-clusters-app/linux --args="chip_tc_required_version=2 chip_tc_required_acknowledgements=3 chip_config_network_layer_ble=false chip_enable_wifi=false is_debug=true" /workspace/connectedhomeip/out/linux-x64-all-clusters-no-ble-no-wifi ninja -C out/linux-x64-all-clusters-no-ble-no-wifi ``` 4. **Build the `chip-tool`**: Configure and compile the `chip-tool`, which is used to commission and control the `chip-all-clusters-app`. ```bash gn gen --check --fail-on-unused-args --export-compile-commands --root=/workspace/connectedhomeip/examples/chip-tool /workspace/connectedhomeip/out/linux-x64-chip-tool ninja -C out/linux-x64-chip-tool ``` 5. **Run the Test Application**: Execute the application, specifying a local Key-Value Store file and enabling trace decoding. ```bash rm -f /tmp/chip_* /tmp/kvs.bin && /workspace/connectedhomeip/out/linux-x64-all-clusters-no-ble-no-wifi/chip-all-clusters-app --KVS=/tmp/kvs.bin --trace_decode 1 ``` 6. **Commissioning Test Application**: Commission the test application with `chip-tool` ```bash /workspace/connectedhomeip/out/linux-x64-chip-tool/chip-tool pairing code 1 34970112332 --trace_decode 1 --country-code US --tc-acknowledgements 1 --tc-acknowledgement-version 1 /workspace/connectedhomeip/out/linux-x64-chip-tool/chip-tool pairing code 1 34970112332 --trace_decode 1 --country-code US --tc-acknowledgements 3 --tc-acknowledgement-version 1 /workspace/connectedhomeip/out/linux-x64-chip-tool/chip-tool pairing code 1 34970112332 --trace_decode 1 --country-code US --tc-acknowledgements 1 --tc-acknowledgement-version 2 /workspace/connectedhomeip/out/linux-x64-chip-tool/chip-tool pairing code 1 34970112332 --trace_decode 1 --country-code US --tc-acknowledgements 3 --tc-acknowledgement-version 2 /workspace/connectedhomeip/out/linux-x64-chip-tool/chip-tool onoff off 1 1 ``` 7. **Probe Test Application**: ```bash /workspace/connectedhomeip/out/linux-x64-chip-tool/chip-tool generalcommissioning read-by-id 5 1 0 /workspace/connectedhomeip/out/linux-x64-chip-tool/chip-tool generalcommissioning read-by-id 6 1 0 /workspace/connectedhomeip/out/linux-x64-chip-tool/chip-tool generalcommissioning read-by-id 7 1 0 /workspace/connectedhomeip/out/linux-x64-chip-tool/chip-tool generalcommissioning set-tcacknowledgements 1 1 1 0 ``` --- .../all-clusters-common/all-clusters-app.zap | 82 ++++++++++- .../commands/pairing/PairingCommand.cpp | 12 +- .../commands/pairing/PairingCommand.h | 12 +- .../general-commissioning-server.cpp | 111 +++++++++++---- src/app/server/BUILD.gn | 6 +- src/app/server/EnhancedSetupFlowProvider.cpp | 106 +++++++++++++++ src/app/server/EnhancedSetupFlowProvider.h | 127 ++++++++++++++++++ src/app/server/TermsAndConditionsProvider.cpp | 79 +++++++++++ src/app/server/TermsAndConditionsProvider.h | 81 +++++++++++ src/controller/AutoCommissioner.cpp | 16 ++- src/controller/CHIPDeviceController.cpp | 35 ++++- src/controller/CHIPDeviceController.h | 5 +- src/controller/CommissioningDelegate.h | 22 ++- src/include/platform/CHIPDeviceConfig.h | 12 +- src/lib/core/BUILD.gn | 4 +- src/platform/device.gni | 10 +- 16 files changed, 682 insertions(+), 38 deletions(-) create mode 100644 src/app/server/EnhancedSetupFlowProvider.cpp create mode 100644 src/app/server/EnhancedSetupFlowProvider.h create mode 100644 src/app/server/TermsAndConditionsProvider.cpp create mode 100644 src/app/server/TermsAndConditionsProvider.h diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap index aec4d4601390e4..167aeaf983037b 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap @@ -2171,6 +2171,22 @@ "source": "server", "isIncoming": 0, "isEnabled": 1 + }, + { + "name": "SetTCAcknowledgements", + "code": 6, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "SetTCAcknowledgementsResponse", + "code": 7, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 } ], "attributes": [ @@ -2254,6 +2270,70 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "TCAcceptedVersion", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TCMinRequiredVersion", + "code": 6, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TCAcknowledgements", + "code": 7, + "mfgCode": null, + "side": "server", + "type": "bitmap16", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0000", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "TCAcknowledgementsRequired", + "code": 8, + "mfgCode": null, + "side": "server", + "type": "boolean", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "true", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "GeneratedCommandList", "code": 65528, @@ -2328,7 +2408,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "0", + "defaultValue": "1", "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/examples/chip-tool/commands/pairing/PairingCommand.cpp b/examples/chip-tool/commands/pairing/PairingCommand.cpp index ed80bc007df796..8bcd8b6ee4c5eb 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"); @@ -118,6 +118,16 @@ CommissioningParameters PairingCommand::GetCommissioningParameters() params.SetCountryCode(CharSpan::fromCharString(mCountryCode.Value())); } + if (mTCAcknowledgements.HasValue()) + { + params.SetTCAcknowledgements(mTCAcknowledgements.Value()); + } + + if (mTCAcknowledgementVersion.HasValue()) + { + params.SetTCAcknowledgementVersion(mTCAcknowledgementVersion.Value()); + } + // 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 0baf70128531b8..98ec583989ad0e 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-acknowledgement-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); @@ -233,6 +241,8 @@ class PairingCommand : public CHIPCommand, chip::Optional mBypassAttestationVerifier; chip::Optional> mCASEAuthTags; chip::Optional mCountryCode; + chip::Optional mTCAcknowledgements; + chip::Optional mTCAcknowledgementVersion; chip::Optional mSkipICDRegistration; chip::Optional mICDCheckInNodeId; chip::Optional mICDSymmetricKey; 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 536ac205454862..461c57c1270230 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. @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -95,9 +96,31 @@ CHIP_ERROR GeneralCommissioningAttrAccess::Read(const ConcreteReadAttributePath case SupportsConcurrentConnection::Id: { return ReadSupportsConcurrentConnection(aEncoder); } - default: { - break; + case TCAcceptedVersion::Id: { + uint16_t tcAcceptedVersion; + CHIP_ERROR err = + EnhancedSetupFlowProvider::GetInstance().GetTermsAndConditionsAcceptedAcknowledgementsVersion(tcAcceptedVersion); + return (CHIP_NO_ERROR != err) ? err : aEncoder.Encode(tcAcceptedVersion); + } + case TCMinRequiredVersion::Id: { + uint16_t tcRequiredVersion; + CHIP_ERROR err = + EnhancedSetupFlowProvider::GetInstance().GetTermsAndConditionsRequiredAcknowledgementsVersion(tcRequiredVersion); + return (CHIP_NO_ERROR != err) ? err : aEncoder.Encode(tcRequiredVersion); + } + case TCAcknowledgements::Id: { + uint16_t tcAcknowledgements; + CHIP_ERROR err = EnhancedSetupFlowProvider::GetInstance().GetTermsAndConditionsAcceptedAcknowledgements(tcAcknowledgements); + return (CHIP_NO_ERROR != err) ? err : aEncoder.Encode(tcAcknowledgements); + } + case TCAcknowledgementsRequired::Id: { + uint16_t tcAcknowledgementsRequired; + CHIP_ERROR err = + EnhancedSetupFlowProvider::GetInstance().GetTermsAndConditionsRequiredAcknowledgements(tcAcknowledgementsRequired); + return (CHIP_NO_ERROR != err) ? err : aEncoder.Encode(tcAcknowledgementsRequired); } + default: + break; } return CHIP_NO_ERROR; } @@ -239,34 +262,43 @@ bool emberAfGeneralCommissioningClusterCommissioningCompleteCallback( } else { - if (failSafe.NocCommandHasBeenInvoked()) + CHIP_ERROR err; + + if (!EnhancedSetupFlowProvider::GetInstance().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 = CommissioningErrorEnum::kRequiredTCNotAccepted; + } + + else if (!EnhancedSetupFlowProvider::GetInstance().HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted()) + { + ChipLogError(AppServer, "Minimum terms and conditions version has not been accepted"); + Breadcrumb::Set(commandPath.mEndpointId, 0); + response.errorCode = CommissioningErrorEnum::kTCMinVersionNotMet; + } + + else + { + 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 +360,40 @@ 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) +{ + MATTER_TRACE_SCOPE("SetTCAcknowledgements", "GeneralCommissioning"); + + Commands::SetTCAcknowledgementsResponse::Type response; + + CheckSuccess( + EnhancedSetupFlowProvider::GetInstance().SetTermsAndConditionsAcceptance(commandData.TCUserResponse, commandData.TCVersion), + Failure); + response.errorCode = CommissioningErrorEnum::kOk; + + commandObj->AddResponse(commandPath, response); + 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; + } + case DeviceLayer::DeviceEventType::kServerReady: { + EnhancedSetupFlowProvider::GetInstance().Init(); + break; + } + default: { + break; + } } } diff --git a/src/app/server/BUILD.gn b/src/app/server/BUILD.gn index 7c661464bbaea3..b22f4a5bd7c1a7 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. @@ -40,10 +40,14 @@ static_library("server") { "Dnssd.h", "EchoHandler.cpp", "EchoHandler.h", + "EnhancedSetupFlowProvider.cpp", + "EnhancedSetupFlowProvider.h", "OnboardingCodesUtil.cpp", "OnboardingCodesUtil.h", "Server.cpp", "Server.h", + "TermsAndConditionsProvider.cpp", + "TermsAndConditionsProvider.h", ] public_configs = [ ":server_config" ] diff --git a/src/app/server/EnhancedSetupFlowProvider.cpp b/src/app/server/EnhancedSetupFlowProvider.cpp new file mode 100644 index 00000000000000..e2a8cdcb3dcdf8 --- /dev/null +++ b/src/app/server/EnhancedSetupFlowProvider.cpp @@ -0,0 +1,106 @@ +/* + * + * 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 "EnhancedSetupFlowProvider.h" + +#include +#include + +chip::app::EnhancedSetupFlowProvider chip::app::EnhancedSetupFlowProvider::sInstance(kRootEndpointId); + +CHIP_ERROR chip::app::EnhancedSetupFlowProvider::Init() +{ + mTermsAndConditionsProvider.Init(); + return CHIP_NO_ERROR; +} + +bool chip::app::EnhancedSetupFlowProvider::HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted() +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + mTermsAndConditionsProvider.GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion); + mTermsAndConditionsProvider.GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion); + + return (requiredAcknowledgements & acceptedAcknowledgements) == requiredAcknowledgements; +} + +bool chip::app::EnhancedSetupFlowProvider::HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted() +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + mTermsAndConditionsProvider.GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion); + mTermsAndConditionsProvider.GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion); + + return acceptedAcknowledgementsVersion >= requiredAcknowledgementsVersion; +} + +CHIP_ERROR chip::app::EnhancedSetupFlowProvider::GetTermsAndConditionsRequiredAcknowledgements(uint16_t & value) +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + + mTermsAndConditionsProvider.GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion); + + value = requiredAcknowledgements; + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::EnhancedSetupFlowProvider::GetTermsAndConditionsRequiredAcknowledgementsVersion(uint16_t & value) +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + + mTermsAndConditionsProvider.GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion); + + value = requiredAcknowledgementsVersion; + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::EnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgements(uint16_t & value) +{ + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + mTermsAndConditionsProvider.GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion); + + value = acceptedAcknowledgements; + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::EnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgementsVersion(uint16_t & value) +{ + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + mTermsAndConditionsProvider.GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion); + + value = acceptedAcknowledgementsVersion; + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::EnhancedSetupFlowProvider::SetTermsAndConditionsAcceptance(uint16_t aTCAcknowledgements, + uint16_t aTCAcknowledgementsVersion) +{ + return mTermsAndConditionsProvider.SetAcceptance(aTCAcknowledgements, aTCAcknowledgementsVersion); +} diff --git a/src/app/server/EnhancedSetupFlowProvider.h b/src/app/server/EnhancedSetupFlowProvider.h new file mode 100644 index 00000000000000..1d5ff992cbb8f7 --- /dev/null +++ b/src/app/server/EnhancedSetupFlowProvider.h @@ -0,0 +1,127 @@ +/* + * + * 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 { + +/** + * @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 Gets the singleton instance of EnhancedSetupFlowProvider. + * + * @return Reference to the singleton instance. + */ + static EnhancedSetupFlowProvider & GetInstance() { return sInstance; } + + /** + * @brief Constructor. + * + * @param[in] endpointId The endpoint identifier. + */ + EnhancedSetupFlowProvider(const chip::EndpointId & endpointId = kRootEndpointId) : mEndpointId(endpointId){}; + + /** + * @brief Destructor. + */ + virtual ~EnhancedSetupFlowProvider() = default; + + /** + * @brief Initializes the EnhancedSetupFlowProvider. + * + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR Init(); + + /** + * @brief Checks if the required terms and conditions acknowledgements have been accepted. + * + * @return true if the required acknowledgements have been accepted, false otherwise. + */ + virtual bool HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(); + + /** + * @brief Checks if the required terms and conditions acknowledgements version has been accepted. + * + * @return true if the required acknowledgements version has been accepted, false otherwise. + */ + virtual bool HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(); + + /** + * @brief Retrieves the required terms and conditions acknowledgements. + * + * @param[out] value The value of the required acknowledgements. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR GetTermsAndConditionsRequiredAcknowledgements(uint16_t & value); + + /** + * @brief Retrieves the required terms and conditions acknowledgements version. + * + * @param[out] value The value 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 & value); + + /** + * @brief Retrieves the accepted terms and conditions acknowledgements. + * + * @param[out] value The value of the accepted acknowledgements. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR GetTermsAndConditionsAcceptedAcknowledgements(uint16_t & value); + + /** + * @brief Retrieves the accepted terms and conditions acknowledgements version. + * + * @param[out] value The value 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 & value); + + /** + * @brief Sets the acceptance status of the terms and conditions. + * + * @param[in] aTCAcknowledgements The acknowledgements to accept. + * @param[in] aTCAcknowledgementsVersion 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 aTCAcknowledgements, uint16_t aTCAcknowledgementsVersion); + +private: + static EnhancedSetupFlowProvider sInstance; /**< Singleton instance of EnhancedSetupFlowProvider. */ + const chip::EndpointId mEndpointId; /**< The endpoint identifier. */ + TermsAndConditionsProvider mTermsAndConditionsProvider; /**< TermsAndConditionsProvider instance. */ +}; + +}; // namespace app +}; // namespace chip diff --git a/src/app/server/TermsAndConditionsProvider.cpp b/src/app/server/TermsAndConditionsProvider.cpp new file mode 100644 index 00000000000000..8b7f94d1f9639c --- /dev/null +++ b/src/app/server/TermsAndConditionsProvider.cpp @@ -0,0 +1,79 @@ +/* + * + * 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 "TermsAndConditionsProvider.h" + +#include +#include + +namespace { +constexpr char kAcceptedAcknowledgementsKeyName[] = "tc-acceptedacknowledgements"; +constexpr char kAcceptedAcknowledgementsVersionKeyName[] = "tc-acceptedacknowledgementsversion"; +} // namespace + +CHIP_ERROR chip::app::TermsAndConditionsProvider::Init() +{ + uint16_t acceptedAcknowledgements = 0; + uint16_t acceptedAcknowledgementsVersion = 0; + + chip::DeviceLayer::PersistedStorage::KeyValueStoreManager & kvsManager = + chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr(); + + kvsManager.Get(kAcceptedAcknowledgementsKeyName, &acceptedAcknowledgements, sizeof(acceptedAcknowledgements)); + kvsManager.Get(kAcceptedAcknowledgementsVersionKeyName, &acceptedAcknowledgementsVersion, + sizeof(acceptedAcknowledgementsVersion)); + + mRequiredAcknowledgements = CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS; + mRequiredAcknowledgementsVersion = CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION; + mAcceptedAcknowledgements = acceptedAcknowledgements; + mAcceptedAcknowledgementsVersion = acceptedAcknowledgementsVersion; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::TermsAndConditionsProvider::GetAcceptance(uint16_t & aAcknowledgements, uint16_t & aAcknowledgementsVersion) +{ + aAcknowledgements = mAcceptedAcknowledgements; + aAcknowledgementsVersion = mAcceptedAcknowledgementsVersion; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::TermsAndConditionsProvider::GetRequirements(uint16_t & aAcknowledgements, uint16_t & aAcknowledgementsVersion) +{ + aAcknowledgements = mRequiredAcknowledgements; + aAcknowledgementsVersion = mRequiredAcknowledgementsVersion; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::TermsAndConditionsProvider::SetAcceptance(uint16_t aTCAcceptedAcknowledgements, + uint16_t aTCAcceptedAcknowledgementsVersion) +{ + chip::DeviceLayer::PersistedStorage::KeyValueStoreManager & kvsManager = + chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr(); + + mAcceptedAcknowledgements = aTCAcceptedAcknowledgements; + mAcceptedAcknowledgementsVersion = aTCAcceptedAcknowledgementsVersion; + + kvsManager.Put(kAcceptedAcknowledgementsKeyName, &mAcceptedAcknowledgements, sizeof(mAcceptedAcknowledgements)); + kvsManager.Put(kAcceptedAcknowledgementsVersionKeyName, &mAcceptedAcknowledgementsVersion, + sizeof(mAcceptedAcknowledgementsVersion)); + + return CHIP_NO_ERROR; +} diff --git a/src/app/server/TermsAndConditionsProvider.h b/src/app/server/TermsAndConditionsProvider.h new file mode 100644 index 00000000000000..61efcb8b898f93 --- /dev/null +++ b/src/app/server/TermsAndConditionsProvider.h @@ -0,0 +1,81 @@ +/* + * + * 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 Initializes the TermsAndConditionsProvider. + * + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR Init(); + + /** + * @brief Retrieves the acceptance status of the required terms and conditions. + * + * @param[out] aAcknowledgements The number of acknowledgements accepted. + * @param[out] aAcknowledgementsVersion 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 & aAcknowledgements, uint16_t & aAcknowledgementsVersion); + + /** + * @brief Retrieves the requirements of the terms and conditions. + * + * @param[out] aAcknowledgements The number of required acknowledgements. + * @param[out] aAcknowledgementsVersion 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 & aAcknowledgements, uint16_t & aAcknowledgementsVersion); + + /** + * @brief Sets the acceptance status of the required terms and conditions. + * + * @param[in] aAcknowledgements The number of acknowledgements to accept. + * @param[in] aAcknowledgementsVersion The version of the acknowledgements to accept. + * @return CHIP_ERROR On success returns CHIP_NO_ERROR, otherwise returns an error code. + */ + virtual CHIP_ERROR SetAcceptance(uint16_t aAcknowledgements, uint16_t aAcknowledgementsVersion); + +private: + uint16_t mRequiredAcknowledgements; /**< The number of required acknowledgements. */ + uint16_t mRequiredAcknowledgementsVersion; /**< The version of the required acknowledgements. */ + uint16_t mAcceptedAcknowledgements; /**< The number of accepted acknowledgements. */ + uint16_t mAcceptedAcknowledgementsVersion; /**< The version of the accepted acknowledgements. */ +}; + +}; // namespace app +}; // namespace chip diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp index 42b39a10c7ab48..9fa5df21484f5b 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"); @@ -343,13 +343,25 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio case CommissioningStage::kArmFailsafe: return CommissioningStage::kConfigRegulatory; case CommissioningStage::kConfigRegulatory: + if (mParams.GetTCAcknowledgements().HasValue() && mParams.GetTCAcknowledgementVersion().HasValue()) + { + return CommissioningStage::kConfigureTCAcknowledgments; + } + else if (mDeviceCommissioningInfo.requiresUTC) + { + return CommissioningStage::kConfigureUTCTime; + } + else + { + return CommissioningStage::kSendPAICertificateRequest; + } + case CommissioningStage::kConfigureTCAcknowledgments: if (mDeviceCommissioningInfo.requiresUTC) { return CommissioningStage::kConfigureUTCTime; } else { - // Time cluster is not supported, move right to DA return CommissioningStage::kSendPAICertificateRequest; } case CommissioningStage::kConfigureUTCTime: diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index af0a66ea834da3..4c058cdc25b13a 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. * @@ -2426,6 +2426,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) { @@ -2874,6 +2890,23 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio } } break; + case CommissioningStage::kConfigureTCAcknowledgments: { + ChipLogProgress(Controller, "Setting Terms and Conditions"); + + GeneralCommissioning::Commands::SetTCAcknowledgements::Type request; + request.TCUserResponse = params.GetTCAcknowledgements().Value(); + request.TCVersion = params.GetTCAcknowledgementVersion().Value(); + 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 dd7b5bc31eec46..d6effac7ad4434 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. * @@ -893,6 +893,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 4b1040fcd79690..656aeb80ddad38 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"); @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace chip { @@ -38,6 +39,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 @@ -158,6 +160,10 @@ class CommissioningParameters // The country code to be used for the node, if set. Optional GetCountryCode() const { return mCountryCode; } + Optional GetTCAcknowledgements() const { return mTCAcknowledgements; } + + Optional GetTCAcknowledgementVersion() const { return mTCAcknowledgementVersion; } + // 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 @@ -330,6 +336,18 @@ class CommissioningParameters return *this; } + CommissioningParameters & SetTCAcknowledgements(uint16_t tcAcknowledgements) + { + mTCAcknowledgements.SetValue(tcAcknowledgements); + return *this; + } + + CommissioningParameters & SetTCAcknowledgementVersion(uint16_t tcAcknowledgementVersion) + { + mTCAcknowledgementVersion.SetValue(tcAcknowledgementVersion); + return *this; + } + // The lifetime of the list buffer needs to exceed the lifetime of the CommissioningParameters object. CommissioningParameters & SetTimeZone(app::DataModel::List timeZone) @@ -594,6 +612,8 @@ class CommissioningParameters Optional mAttestationNonce; Optional mWiFiCreds; Optional mCountryCode; + Optional mTCAcknowledgements; + Optional mTCAcknowledgementVersion; Optional mThreadOperationalDataset; Optional mNOCChainGenerationParameters; Optional mRootCert; diff --git a/src/include/platform/CHIPDeviceConfig.h b/src/include/platform/CHIPDeviceConfig.h index 785b47656dd5a3..d7be4de182ebbc 100644 --- a/src/include/platform/CHIPDeviceConfig.h +++ b/src/include/platform/CHIPDeviceConfig.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * Copyright (c) 2019-2020 Google LLC. * Copyright (c) 2018 Nest Labs, Inc. * @@ -1507,6 +1507,16 @@ static_assert(CHIP_DEVICE_CONFIG_BLE_EXT_ADVERTISING_INTERVAL_MIN <= CHIP_DEVICE // -------------------- Miscellaneous -------------------- +// ----------------------- Terms and Conditions ----------------------- + +#ifndef CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS +#define CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS 0 +#endif + +#ifndef CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION +#define CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION 0 +#endif + /** * CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES * diff --git a/src/lib/core/BUILD.gn b/src/lib/core/BUILD.gn index eaecf859ac1993..eb200ce17ea4a4 100644 --- a/src/lib/core/BUILD.gn +++ b/src/lib/core/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2020-2022 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. @@ -69,6 +69,8 @@ buildconfig_header("chip_buildconfig") { "CHIP_CONFIG_TLV_VALIDATE_CHAR_STRING_ON_WRITE=${chip_tlv_validate_char_string_on_write}", "CHIP_CONFIG_TLV_VALIDATE_CHAR_STRING_ON_READ=${chip_tlv_validate_char_string_on_read}", "CHIP_CONFIG_COMMAND_SENDER_BUILTIN_SUPPORT_FOR_BATCHED_COMMANDS=${chip_enable_sending_batch_commands}", + "CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS=${chip_tc_required_acknowledgements}", + "CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION=${chip_tc_required_version}", ] visibility = [ ":chip_config_header" ] diff --git a/src/platform/device.gni b/src/platform/device.gni index 01358d4880f876..ece6774887f3c0 100644 --- a/src/platform/device.gni +++ b/src/platform/device.gni @@ -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. @@ -225,6 +225,14 @@ declare_args() { build_tv_casting_common_a = false } +declare_args() { + # If non-zero, is the terms and conditions required acknowledgements bitfield + chip_tc_required_acknowledgements = 0 + + # If non-zero, is the terms and conditions required version + chip_tc_required_version = 0 +} + assert(!chip_disable_platform_kvs || chip_device_platform == "darwin", "Can only disable KVS on some platforms")