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 Oct 11, 2024
1 parent 1988399 commit 4f06d22
Show file tree
Hide file tree
Showing 17 changed files with 1,370 additions and 20 deletions.
18 changes: 17 additions & 1 deletion src/app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ declare_args() {
current_os != "android"
}

config("enhanced_setup_flow_config") {
defines = []
if (chip_config_tc_required) {
defines += [
"CHIP_CONFIG_TC_REQUIRED=1",
"CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS=${chip_config_tc_required_acknowledgements}",
"CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION=${chip_config_tc_required_acknowledgements_version}",
]
} else {
defines += [ "CHIP_CONFIG_TC_REQUIRED=0" ]
}
}

buildconfig_header("app_buildconfig") {
header = "AppBuildConfig.h"
header_dir = "app"
Expand Down Expand Up @@ -522,5 +535,8 @@ static_library("app") {

cflags = [ "-Wconversion" ]

public_configs = [ "${chip_root}/src:includes" ]
public_configs = [
":enhanced_setup_flow_config",
"${chip_root}/src:includes",
]
}
9 changes: 6 additions & 3 deletions src/app/FailSafeContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,12 @@ void FailSafeContext::ScheduleFailSafeCleanup(FabricIndex fabricIndex, bool addN
SetFailSafeArmed(false);

ChipDeviceEvent event{ .Type = DeviceEventType::kFailSafeTimerExpired,
.FailSafeTimerExpired = { .fabricIndex = fabricIndex,
.addNocCommandHasBeenInvoked = addNocCommandInvoked,
.updateNocCommandHasBeenInvoked = updateNocCommandInvoked } };
.FailSafeTimerExpired = {
.fabricIndex = fabricIndex,
.addNocCommandHasBeenInvoked = addNocCommandInvoked,
.updateNocCommandHasBeenInvoked = updateNocCommandInvoked,
.updateTermsAndConditionsHasBeenInvoked = mUpdateTermsAndConditionsHasBeenInvoked,
} };
CHIP_ERROR status = PlatformMgr().PostEvent(&event);

if (status != CHIP_NO_ERROR)
Expand Down
18 changes: 11 additions & 7 deletions src/app/FailSafeContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class FailSafeContext
void SetUpdateNocCommandInvoked() { mUpdateNocCommandHasBeenInvoked = true; }
void SetAddTrustedRootCertInvoked() { mAddTrustedRootCertHasBeenInvoked = true; }
void SetCsrRequestForUpdateNoc(bool isForUpdateNoc) { mIsCsrRequestForUpdateNoc = isForUpdateNoc; }
void SetUpdateTermsAndConditionsHasBeenInvoked() { mUpdateTermsAndConditionsHasBeenInvoked = true; }

/**
* @brief
Expand Down Expand Up @@ -91,6 +92,7 @@ class FailSafeContext
bool UpdateNocCommandHasBeenInvoked() const { return mUpdateNocCommandHasBeenInvoked; }
bool AddTrustedRootCertHasBeenInvoked() const { return mAddTrustedRootCertHasBeenInvoked; }
bool IsCsrRequestForUpdateNoc() const { return mIsCsrRequestForUpdateNoc; }
bool UpdateTermsAndConditionsHasBeenInvoked() { return mUpdateTermsAndConditionsHasBeenInvoked; }

FabricIndex GetFabricIndex() const
{
Expand All @@ -109,8 +111,9 @@ class FailSafeContext
bool mUpdateNocCommandHasBeenInvoked = false;
bool mAddTrustedRootCertHasBeenInvoked = false;
// The fact of whether a CSR occurred at all is stored elsewhere.
bool mIsCsrRequestForUpdateNoc = false;
FabricIndex mFabricIndex = kUndefinedFabricIndex;
bool mIsCsrRequestForUpdateNoc = false;
FabricIndex mFabricIndex = kUndefinedFabricIndex;
bool mUpdateTermsAndConditionsHasBeenInvoked = false;

/**
* @brief
Expand Down Expand Up @@ -140,11 +143,12 @@ class FailSafeContext
{
SetFailSafeArmed(false);

mAddNocCommandHasBeenInvoked = false;
mUpdateNocCommandHasBeenInvoked = false;
mAddTrustedRootCertHasBeenInvoked = false;
mFailSafeBusy = false;
mIsCsrRequestForUpdateNoc = false;
mAddNocCommandHasBeenInvoked = false;
mUpdateNocCommandHasBeenInvoked = false;
mAddTrustedRootCertHasBeenInvoked = false;
mFailSafeBusy = false;
mIsCsrRequestForUpdateNoc = false;
mUpdateTermsAndConditionsHasBeenInvoked = false;
}

void FailSafeTimerExpired();
Expand Down
3 changes: 2 additions & 1 deletion src/app/chip_data_model.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ function(chip_configure_data_model APP_TARGET)
# CMAKE data model auto-includes the server side implementation
target_sources(${APP_TARGET} ${SCOPE}
${CHIP_APP_BASE_DIR}/server/AclStorage.cpp
${CHIP_APP_BASE_DIR}/server/DefaultAclStorage.cpp
${CHIP_APP_BASE_DIR}/server/CommissioningWindowManager.cpp
${CHIP_APP_BASE_DIR}/server/DefaultAclStorage.cpp
${CHIP_APP_BASE_DIR}/server/DefaultTermsAndConditionsProvider.cpp
${CHIP_APP_BASE_DIR}/server/Dnssd.cpp
${CHIP_APP_BASE_DIR}/server/EchoHandler.cpp
${CHIP_APP_BASE_DIR}/server/OnboardingCodesUtil.cpp
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 Down Expand Up @@ -28,7 +28,9 @@
#include <app/ConcreteCommandPath.h>
#include <app/server/CommissioningWindowManager.h>
#include <app/server/Server.h>
#include <app/server/TermsAndConditionsProvider.h>
#include <app/util/attribute-storage.h>
#include <credentials/FabricTable.h>
#include <lib/support/Span.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/CHIPDeviceConfig.h>
Expand Down Expand Up @@ -95,6 +97,38 @@ CHIP_ERROR GeneralCommissioningAttrAccess::Read(const ConcreteReadAttributePath
case SupportsConcurrentConnection::Id: {
return ReadSupportsConcurrentConnection(aEncoder);
}
case TCAcceptedVersion::Id: {
Optional<TermsAndConditions> outTermsAndConditions;
Server::GetInstance().GetTermsAndConditionsProvider()->GetAcceptance(outTermsAndConditions);
return !outTermsAndConditions.HasValue() ? aEncoder.Encode(static_cast<uint16_t>(0))
: aEncoder.Encode(outTermsAndConditions.Value().version);
}
case TCMinRequiredVersion::Id: {
Optional<TermsAndConditions> outTermsAndConditions;
Server::GetInstance().GetTermsAndConditionsProvider()->GetRequirements(outTermsAndConditions);
return !outTermsAndConditions.HasValue() ? aEncoder.Encode(static_cast<uint16_t>(0))
: aEncoder.Encode(outTermsAndConditions.Value().version);
}
case TCAcknowledgements::Id: {
Optional<TermsAndConditions> outTermsAndConditions;
Server::GetInstance().GetTermsAndConditionsProvider()->GetAcceptance(outTermsAndConditions);
return !outTermsAndConditions.HasValue() ? aEncoder.Encode(static_cast<uint16_t>(0))
: aEncoder.Encode(outTermsAndConditions.Value().value);
}
case TCAcknowledgementsRequired::Id: {
Optional<TermsAndConditions> outTermsAndConditions;
Server::GetInstance().GetTermsAndConditionsProvider()->GetAcceptance(outTermsAndConditions);

TermsAndConditionsState termsAndConditionsState;
Server::GetInstance().GetTermsAndConditionsProvider()->CheckAcceptance(outTermsAndConditions, termsAndConditionsState);

bool setTermsAndConditionsCallRequiredBeforeCommissioningCompleteSuccess =
termsAndConditionsState != TermsAndConditionsState::OK;
return aEncoder.Encode(setTermsAndConditionsCallRequiredBeforeCommissioningCompleteSuccess);
}
case TCUpdateDeadline::Id: {
return aEncoder.EncodeNull();
}
default: {
break;
}
Expand Down Expand Up @@ -144,6 +178,32 @@ CHIP_ERROR GeneralCommissioningAttrAccess::ReadSupportsConcurrentConnection(Attr
return aEncoder.Encode(supportsConcurrentConnection);
}

CommissioningErrorEnum CheckTermsAndConditionsAcknowledgementsState(TermsAndConditionsProvider * const termsAndConditionsProvider,
const Optional<TermsAndConditions> & acceptedTermsAndConditions)
{
TermsAndConditionsState termsAndConditionsState;

CHIP_ERROR err = termsAndConditionsProvider->CheckAcceptance(acceptedTermsAndConditions, termsAndConditionsState);
if (CHIP_NO_ERROR != err)
{
return chip::app::Clusters::GeneralCommissioning::CommissioningErrorEnum::kUnknownEnumValue;
}

switch (termsAndConditionsState)
{
case OK:
return chip::app::Clusters::GeneralCommissioning::CommissioningErrorEnum::kOk;
case TC_ACKNOWLEDGEMENTS_NOT_RECEIVED:
return chip::app::Clusters::GeneralCommissioning::CommissioningErrorEnum::kTCAcknowledgementsNotReceived;
case TC_MIN_VERSION_NOT_MET:
return chip::app::Clusters::GeneralCommissioning::CommissioningErrorEnum::kTCMinVersionNotMet;
case REQUIRED_TC_NOT_ACCEPTED:
return chip::app::Clusters::GeneralCommissioning::CommissioningErrorEnum::kRequiredTCNotAccepted;
}

return chip::app::Clusters::GeneralCommissioning::CommissioningErrorEnum::kOk;
}

} // anonymous namespace

bool emberAfGeneralCommissioningClusterArmFailSafeCallback(app::CommandHandler * commandObj,
Expand Down Expand Up @@ -218,6 +278,8 @@ bool emberAfGeneralCommissioningClusterCommissioningCompleteCallback(
auto & failSafe = Server::GetInstance().GetFailSafeContext();
auto & fabricTable = Server::GetInstance().GetFabricTable();

CHIP_ERROR err;

ChipLogProgress(FailSafe, "GeneralCommissioning: Received CommissioningComplete");

Commands::CommissioningCompleteResponse::Type response;
Expand All @@ -239,9 +301,44 @@ bool emberAfGeneralCommissioningClusterCommissioningCompleteCallback(
}
else
{
TermsAndConditionsProvider * const termsAndConditionsProvider = Server::GetInstance().GetTermsAndConditionsProvider();
Optional<TermsAndConditions> acceptedTermsAndConditions;

err = termsAndConditionsProvider->GetAcceptance(acceptedTermsAndConditions);
if (err != CHIP_NO_ERROR)
{
ChipLogError(FailSafe, "GeneralCommissioning: Failed to get terms and conditions acceptance: %" CHIP_ERROR_FORMAT,
err.Format());
}
CheckSuccess(err, Failure);

response.errorCode =
CheckTermsAndConditionsAcknowledgementsState(termsAndConditionsProvider, acceptedTermsAndConditions);
if (CommissioningErrorEnum::kOk != response.errorCode)
{
commandObj->AddResponse(commandPath, response);
return true;
}

if (failSafe.UpdateTermsAndConditionsHasBeenInvoked())
{
// Commit terms and conditions acceptance on commissioning complete
err = Server::GetInstance().GetTermsAndConditionsProvider()->CommitAcceptance();
if (err != CHIP_NO_ERROR)
{
ChipLogError(FailSafe, "GeneralCommissioning: Failed to commit terms and conditions: %" CHIP_ERROR_FORMAT,
err.Format());
}
else
{
ChipLogProgress(FailSafe, "GeneralCommissioning: Successfully commited terms and conditions");
}
CheckSuccess(err, Failure);
}

if (failSafe.NocCommandHasBeenInvoked())
{
CHIP_ERROR err = fabricTable.CommitPendingFabricData();
err = fabricTable.CommitPendingFabricData();
if (err != CHIP_NO_ERROR)
{
// No need to revert on error: CommitPendingFabricData always reverts if not fully successful.
Expand Down Expand Up @@ -328,23 +425,84 @@ 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");

auto & failSafeContext = Server::GetInstance().GetFailSafeContext();
TermsAndConditionsProvider * const termsAndConditionsProvider = Server::GetInstance().GetTermsAndConditionsProvider();

Optional<TermsAndConditions> acceptedTermsAndConditions = Optional<TermsAndConditions>({
.value = commandData.TCUserResponse,
.version = commandData.TCVersion,
});

Commands::SetTCAcknowledgementsResponse::Type response;
response.errorCode = CheckTermsAndConditionsAcknowledgementsState(termsAndConditionsProvider, acceptedTermsAndConditions);

if (CommissioningErrorEnum::kOk == response.errorCode)
{
CheckSuccess(termsAndConditionsProvider->SetAcceptance(acceptedTermsAndConditions), Failure);

if (failSafeContext.IsFailSafeArmed())
{
failSafeContext.SetUpdateTermsAndConditionsHasBeenInvoked();
}
else
{
CheckSuccess(termsAndConditionsProvider->CommitAcceptance(), Failure);
}
}

commandObj->AddResponse(commandPath, response);
return true;
}

namespace {
void OnPlatformEventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg)
{
if (event->Type == DeviceLayer::DeviceEventType::kFailSafeTimerExpired)
{
// Spec says to reset Breadcrumb attribute to 0.
Breadcrumb::Set(0, 0);

if (event->FailSafeTimerExpired.updateTermsAndConditionsHasBeenInvoked)
{
// Clear terms and conditions acceptance on failsafe timer expiration
Server::GetInstance().GetTermsAndConditionsProvider()->RevertAcceptance();
}
}
}

} // anonymous namespace

class GeneralCommissioningFabricTableDelegate : public chip::FabricTable::Delegate
{
public:
// Gets called when a fabric is deleted
void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override
{
// If the FabricIndex matches the last remaining entry in the Fabrics list, then the device SHALL delete all Matter
// related data on the node which was created since it was commissioned.
if (Server::GetInstance().GetFabricTable().FabricCount() == 0)
{
ChipLogProgress(Zcl, "general-commissioning-server: Last Fabric index 0x%x was removed",
static_cast<unsigned>(fabricIndex));
Server::GetInstance().GetTermsAndConditionsProvider()->ResetAcceptance();
}
}
};

void MatterGeneralCommissioningPluginServerInitCallback()
{
Breadcrumb::Set(0, 0);
AttributeAccessInterfaceRegistry::Instance().Register(&gAttrAccess);
DeviceLayer::PlatformMgrImpl().AddEventHandler(OnPlatformEventHandler);

static GeneralCommissioningFabricTableDelegate generalCommissioningFabricTableDelegate;
Server::GetInstance().GetFabricTable().AddFabricDelegate(&generalCommissioningFabricTableDelegate);
}

namespace chip {
Expand Down
23 changes: 23 additions & 0 deletions src/app/common_flags.gni
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,29 @@ declare_args() {
chip_enable_read_client = true
chip_build_controller_dynamic_server = false

# Configures whether terms and conditions acknowledgements are required.
#
# When set to true, the application will enforce the requirement of
# terms and conditions acknowledgements during commissioning.
chip_config_tc_required = false

# Configures the required terms and conditions acknowledgements bitmask.
#
# This setting defines the required terms and conditions acknowledgements bitmask.
# The bit-field is 16 bits long, so the possible value range is [0, 65535).
# This setting can be used to require that terms and conditions are presented
# to the user during commissioning.
chip_config_tc_required_acknowledgements = 0

# Configures the latest known version of the terms and conditions.
#
# This setting defines the version number of the latest terms and conditions.
# It allows the application to iterate on revisions of the terms and conditions.
# A value of 0 indicates that no specific version is required. This setting can
# be used to enforce version-specific terms and conditions acknowledgements in
# the application.
chip_config_tc_required_acknowledgements_version = 0

# Flag that controls whether the time-to-wait from BUSY responses is
# communicated to OperationalSessionSetup API consumers.
chip_enable_busy_handling_for_operational_session_setup = true
Expand Down
5 changes: 4 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 @@ -37,6 +37,8 @@ static_library("server") {
"CommissioningWindowManager.h",
"DefaultAclStorage.cpp",
"DefaultAclStorage.h",
"DefaultTermsAndConditionsProvider.cpp",
"DefaultTermsAndConditionsProvider.h",
"Dnssd.cpp",
"Dnssd.h",
"EchoHandler.cpp",
Expand All @@ -45,6 +47,7 @@ static_library("server") {
"OnboardingCodesUtil.h",
"Server.cpp",
"Server.h",
"TermsAndConditionsProvider.h",
]

public_configs = [ ":server_config" ]
Expand Down
Loading

0 comments on commit 4f06d22

Please sign in to comment.