Skip to content

Commit

Permalink
Add initial feature logic for Terms and Conditions (TC) acknowledgements
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
swan-amazon committed Jun 24, 2024
1 parent 50971be commit a27bb1d
Show file tree
Hide file tree
Showing 20 changed files with 1,558 additions and 129 deletions.
13 changes: 12 additions & 1 deletion examples/chip-tool/commands/pairing/PairingCommand.cpp
Original file line number Diff line number Diff line change
@@ -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");
Expand Down Expand Up @@ -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 <chip::Optional> class,
// we will use mTimeZoneList.data() value to determine if the argument was provided.
Expand Down
12 changes: 11 additions & 1 deletion examples/chip-tool/commands/pairing/PairingCommand.h
Original file line number Diff line number Diff line change
@@ -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");
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -238,6 +246,8 @@ class PairingCommand : public CHIPCommand,
chip::Optional<chip::ByteSpan> mICDSymmetricKey;
chip::Optional<uint64_t> mICDMonitoredSubject;
chip::Optional<uint32_t> mICDStayActiveDurationMsec;
chip::Optional<uint16_t> mTCAcknowledgements;
chip::Optional<uint16_t> mTCAcknowledgementVersion;
chip::app::DataModel::List<chip::app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type> mTimeZoneList;
TypedComplexArgument<chip::app::DataModel::List<chip::app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type>>
mComplex_TimeZones;
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -27,13 +27,15 @@
#include <app/CommandHandler.h>
#include <app/ConcreteCommandPath.h>
#include <app/server/CommissioningWindowManager.h>
#include <app/server/EnhancedSetupFlowProvider.h>
#include <app/server/Server.h>
#include <app/util/attribute-storage.h>
#include <lib/support/Span.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/CHIPDeviceConfig.h>
#include <platform/ConfigurationManager.h>
#include <platform/DeviceControlServer.h>
#include <system/SystemConfig.h>
#include <tracing/macros.h>

using namespace chip;
Expand All @@ -57,6 +59,55 @@ using Transport::Session;

namespace {

template <typename T, typename K>
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 <typename Provider, typename T>
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<CHIP_ERROR (Provider::*const)(T &) const>(getter);
return ReadInternal(constProvider, constGetter, aEncoder);
}

template <typename T, typename K>
static CHIP_ERROR ReadInternal(const T & provider, CHIP_ERROR (T::*getter)(K &) const, AttributeValueEncoder & aEncoder)
{
return ReadInternal(&provider, getter, aEncoder);
}

template <typename T, typename K>
static CHIP_ERROR ReadInternal(T & provider, CHIP_ERROR (T::*getter)(K &), AttributeValueEncoder & aEncoder)
{
return ReadInternal(&provider, getter, aEncoder);
}

template <typename... Args>
static CHIP_ERROR ReadIfSupported(Args &&... args)
{
return ReadInternal(std::forward<Args>(args)...);
}

class GeneralCommissioningAttrAccess : public AttributeAccessInterface
{
public:
Expand All @@ -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);
};
Expand All @@ -84,39 +134,43 @@ 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);
}
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)
Expand Down Expand Up @@ -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");

Expand All @@ -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;
}
}
}

Expand Down Expand Up @@ -328,13 +415,37 @@ bool emberAfGeneralCommissioningClusterSetRegulatoryConfigCallback(app::CommandH
return true;
}

#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION
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;
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);
return true;
}
#endif

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;
}
}
}

Expand Down
8 changes: 7 additions & 1 deletion src/app/server/BUILD.gn
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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" ]
Expand Down
Loading

0 comments on commit a27bb1d

Please sign in to comment.