From 3524a2ebcbf5b892581602d80dd8b3d3295171ab Mon Sep 17 00:00:00 2001 From: James Swan <122404367+swan-amazon@users.noreply.github.com> Date: Thu, 29 Aug 2024 19:31:40 +0000 Subject: [PATCH] Enable Terms and Conditions (TC) feature for AutoCommissioner This commit enables the Terms and Conditions (TC) feature in the AutoCommissioner module. The changes ensure that the client can handle TC acknowledgements and enforce required terms and conditions during the commissioning process. This enables the TC feature logic in the example app, building on the initial implementation. --- .../commands/pairing/PairingCommand.cpp | 16 +++- .../commands/pairing/PairingCommand.h | 17 ++++- src/controller/AutoCommissioner.cpp | 6 +- src/controller/CHIPDeviceController.cpp | 74 ++++++++++++++++++- src/controller/CHIPDeviceController.h | 24 +++++- src/controller/CommissioningDelegate.cpp | 6 ++ src/controller/CommissioningDelegate.h | 37 +++++++++- 7 files changed, 172 insertions(+), 8 deletions(-) diff --git a/examples/chip-tool/commands/pairing/PairingCommand.cpp b/examples/chip-tool/commands/pairing/PairingCommand.cpp index b9034cc1285d49..2bc69a6c93521b 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"); @@ -129,6 +129,20 @@ CommissioningParameters PairingCommand::GetCommissioningParameters() params.SetCountryCode(CharSpan::fromCharString(mCountryCode.Value())); } + // Default requiring TCs to false, to preserve release 1.3 chip-tool behavior + params.SetRequireTermsAndConditionsAcknowledgement(mRequireTCAcknowledgements.ValueOr(false)); + + // 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 9965b663ec111c..b21f13067da4ed 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"); @@ -202,6 +202,18 @@ class PairingCommand : public CHIPCommand, AddArgument("dst-offset", &mComplex_DSTOffsets, "DSTOffset list to use when setting Time Synchronization cluster's DSTOffset attribute", Argument::kOptional); + + AddArgument("require-tc-acknowledgements", 0, 1, &mRequireTCAcknowledgements, + "Terms and Conditions acknowledgements is known to be required or not, (when required, the commissioner " + "will wait until they are provided)"); + + 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); @@ -259,6 +271,9 @@ class PairingCommand : public CHIPCommand, chip::Optional mICDMonitoredSubject; chip::Optional mICDClientType; chip::Optional mICDStayActiveDurationMsec; + chip::Optional mRequireTCAcknowledgements; + chip::Optional mTCAcknowledgements; + chip::Optional mTCAcknowledgementVersion; chip::app::DataModel::List mTimeZoneList; TypedComplexArgument> mComplex_TimeZones; diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp index 64f132c3124cda..81c4701b0be024 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"); @@ -365,6 +365,10 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio case CommissioningStage::kArmFailsafe: return CommissioningStage::kConfigRegulatory; case CommissioningStage::kConfigRegulatory: + return CommissioningStage::kGetTCAcknowledgments; + case CommissioningStage::kGetTCAcknowledgments: + 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 346867226e4261..bb7730cf425316 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. * @@ -2688,6 +2688,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) { @@ -2763,6 +2779,16 @@ CHIP_ERROR DeviceCommissioner::ICDRegistrationInfoReady() return CHIP_NO_ERROR; } +CHIP_ERROR DeviceCommissioner::TermsAndConditionsAcknowledgementsReady() +{ + ReturnErrorCodeIf(mCommissioningStage != CommissioningStage::kGetTCAcknowledgments, CHIP_ERROR_INCORRECT_STATE); + + // need to advance to next step + CommissioningStageComplete(CHIP_NO_ERROR); + + return CHIP_NO_ERROR; +} + void DeviceCommissioner::OnNetworkConfigResponse(void * context, const NetworkCommissioning::Commands::NetworkConfigResponse::DecodableType & data) { @@ -3160,6 +3186,52 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio } } break; + case CommissioningStage::kGetTCAcknowledgments: { + ChipLogProgress(Controller, "Get Terms and Conditions Acknowledgments"); + + // If terms and conditions acknowledgements are not required, or have already been provided, then proceed + if (!params.GetRequireTermsAndConditionsAcknowledgement() || params.GetTermsAndConditionsAcknowledgement().HasValue()) + { + TermsAndConditionsAcknowledgementsReady(); + return; + } + + ChipLogProgress(Controller, "Waiting for Terms and Conditions"); + break; + } + case CommissioningStage::kConfigureTCAcknowledgments: { + ChipLogProgress(Controller, "Setting Terms and Conditions"); + + if (!params.GetRequireTermsAndConditionsAcknowledgement()) + { + ChipLogProgress(Controller, "Setting Terms and Conditions: Skipped"); + CommissioningStageComplete(CHIP_NO_ERROR); + return; + } + + if (!params.GetTermsAndConditionsAcknowledgement().HasValue()) + { + ChipLogError(Controller, "No acknowledgements provided"); + CommissioningStageComplete(CHIP_ERROR_INCORRECT_STATE); + return; + } + + GeneralCommissioning::Commands::SetTCAcknowledgements::Type request; + TermsAndConditionsAcknowledgement termsAndConditionsAcknowledgement = params.GetTermsAndConditionsAcknowledgement().Value(); + request.TCUserResponse = termsAndConditionsAcknowledgement.acceptedTermsAndConditions; + request.TCVersion = termsAndConditionsAcknowledgement.acceptedTermsAndConditionsVersion; + + ChipLogProgress(Controller, "Setting Terms and Conditions: %hu, %hu", request.TCUserResponse, request.TCVersion); + 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 2ec020340ee98d..48d02b885b2e78 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. * @@ -696,6 +696,25 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, */ CHIP_ERROR ICDRegistrationInfoReady(); + /** + * @brief + * This function is called by the upper layer application to indicate that the required terms and conditions + * acknowledgements have been set. This function should be called after the terms and conditions bitmask and version + * have been defined using the appropriate configuration macros and the application has gathered the necessary + * acknowledgements from the user. + * + * The upper layer application should call this method once it has successfully presented and obtained acknowledgements + * for the required terms and conditions from the user. This indicates that the commissioning process can advance to the + * next stage. + * + * When the terms and conditions acknowledgements process is completed, this function will signal the readiness to proceed + * to the next step in the commissioning process. + * + * @return CHIP_ERROR The return status. Returns CHIP_ERROR_INCORRECT_STATE if the function is called when the device + * is not in the correct state to accept terms and conditions acknowledgements. + */ + CHIP_ERROR TermsAndConditionsAcknowledgementsReady(); + /** * @brief * This function returns the current CommissioningStage for this commissioner. @@ -947,6 +966,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.cpp b/src/controller/CommissioningDelegate.cpp index d6acac6bc00bd4..bba23bf7da03f8 100644 --- a/src/controller/CommissioningDelegate.cpp +++ b/src/controller/CommissioningDelegate.cpp @@ -46,6 +46,12 @@ const char * StageToString(CommissioningStage stage) case kConfigRegulatory: return "ConfigRegulatory"; + case kGetTCAcknowledgments: + return "GetTCAcknowledgments"; + + case kConfigureTCAcknowledgments: + return "ConfigureTCAcknowledgments"; + case kConfigureUTCTime: return "ConfigureUTCTime"; diff --git a/src/controller/CommissioningDelegate.h b/src/controller/CommissioningDelegate.h index f0f98961adf3c3..aa0c73b3a63699 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"); @@ -70,7 +70,6 @@ enum CommissioningStage : uint8_t ///< Commissioning Complete command kSendComplete, ///< Send CommissioningComplete (0x30:4) command to the device kICDSendStayActive, ///< Send Keep Alive to ICD - kCleanup, ///< Call delegates with status, free memory, clear timers and state /// Send ScanNetworks (0x31:0) command to the device. /// ScanNetworks can happen anytime after kArmFailsafe. /// However, the cirque tests fail if it is earlier in the list @@ -82,7 +81,10 @@ enum CommissioningStage : uint8_t kPrimaryOperationalNetworkFailed, ///< Indicate that the primary operational network (on root endpoint) failed, should remove ///< the primary network config later. kRemoveWiFiNetworkConfig, ///< Remove Wi-Fi network config. - kRemoveThreadNetworkConfig ///< Remove Thread network config. + kRemoveThreadNetworkConfig, ///< Remove Thread network config. + kGetTCAcknowledgments, ///< Waiting for the higher layer to provide terms and conditions acknowledgements. + kConfigureTCAcknowledgments, ///< Send SetTCAcknowledgements (0x30:6) command to the device + kCleanup, ///< Call delegates with status, free memory, clear timers and state }; enum class ICDRegistrationStrategy : uint8_t @@ -105,6 +107,12 @@ struct WiFiCredentials WiFiCredentials(ByteSpan newSsid, ByteSpan newCreds) : ssid(newSsid), credentials(newCreds) {} }; +struct TermsAndConditionsAcknowledgement +{ + uint16_t acceptedTermsAndConditions; + uint16_t acceptedTermsAndConditionsVersion; +}; + struct NOCChainGenerationParameters { ByteSpan nocsrElements; @@ -169,6 +177,13 @@ class CommissioningParameters // The country code to be used for the node, if set. Optional GetCountryCode() const { return mCountryCode; } + bool GetRequireTermsAndConditionsAcknowledgement() const { return mRequireTermsAndConditionsAcknowledgement; } + + 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 @@ -341,6 +356,19 @@ class CommissioningParameters return *this; } + CommissioningParameters & SetRequireTermsAndConditionsAcknowledgement(bool requireTermsAndConditionsAcknowledgement) + { + mRequireTermsAndConditionsAcknowledgement = requireTermsAndConditionsAcknowledgement; + 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) @@ -582,6 +610,7 @@ class CommissioningParameters mAttestationNonce.ClearValue(); mWiFiCreds.ClearValue(); mCountryCode.ClearValue(); + mTermsAndConditionsAcknowledgement.ClearValue(); mThreadOperationalDataset.ClearValue(); mNOCChainGenerationParameters.ClearValue(); mRootCert.ClearValue(); @@ -612,6 +641,7 @@ class CommissioningParameters Optional mAttestationNonce; Optional mWiFiCreds; Optional mCountryCode; + Optional mTermsAndConditionsAcknowledgement; Optional mThreadOperationalDataset; Optional mNOCChainGenerationParameters; Optional mRootCert; @@ -644,6 +674,7 @@ class CommissioningParameters Optional mICDStayActiveDurationMsec; ICDRegistrationStrategy mICDRegistrationStrategy = ICDRegistrationStrategy::kIgnore; bool mCheckForMatchingFabric = false; + bool mRequireTermsAndConditionsAcknowledgement = false; }; struct RequestedCertificate