diff --git a/examples/chip-tool/commands/pairing/PairingCommand.cpp b/examples/chip-tool/commands/pairing/PairingCommand.cpp index 245c9ed57ff82c..ab76c985339662 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 0e83d7125cf175..cfdd5826919a8b 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 955e36bfb0d8a2..c1b788aca643bd 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. * @@ -2691,6 +2691,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) { @@ -2766,6 +2782,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) { @@ -3163,6 +3189,49 @@ 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()) + { + 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; + 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 4b876156199735..1f1b381da96cbd 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 85ea5e86c5e3a6..ed7e558828db1b 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 9ef40a6e20e408..a707b05f873385 100644 --- a/src/controller/CommissioningDelegate.h +++ b/src/controller/CommissioningDelegate.h @@ -106,6 +106,12 @@ struct WiFiCredentials WiFiCredentials(ByteSpan newSsid, ByteSpan newCreds) : ssid(newSsid), credentials(newCreds) {} }; +struct TermsAndConditionsAcknowledgement +{ + uint16_t acceptedTermsAndConditions; + uint16_t acceptedTermsAndConditionsVersion; +}; + struct NOCChainGenerationParameters { ByteSpan nocsrElements; @@ -170,6 +176,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 @@ -342,6 +355,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) @@ -583,6 +609,7 @@ class CommissioningParameters mAttestationNonce.ClearValue(); mWiFiCreds.ClearValue(); mCountryCode.ClearValue(); + mTermsAndConditionsAcknowledgement.ClearValue(); mThreadOperationalDataset.ClearValue(); mNOCChainGenerationParameters.ClearValue(); mRootCert.ClearValue(); @@ -613,6 +640,7 @@ class CommissioningParameters Optional mAttestationNonce; Optional mWiFiCreds; Optional mCountryCode; + Optional mTermsAndConditionsAcknowledgement; Optional mThreadOperationalDataset; Optional mNOCChainGenerationParameters; Optional mRootCert; @@ -645,6 +673,7 @@ class CommissioningParameters Optional mICDStayActiveDurationMsec; ICDRegistrationStrategy mICDRegistrationStrategy = ICDRegistrationStrategy::kIgnore; bool mCheckForMatchingFabric = false; + bool mRequireTermsAndConditionsAcknowledgement = false; }; struct RequestedCertificate