Skip to content

Commit

Permalink
Integrate Terms and Conditions Acknowledgements in Commissioning Process
Browse files Browse the repository at this point in the history
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 [email protected]: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_acknowledgements=1 chip_tc_required_acknowledgements_version=1 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 --tc-acknowledgements 1 --tc-acknowledgements-version 1
    /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
    ```
  • Loading branch information
swan-amazon committed Jun 17, 2024
1 parent 8ba371a commit 2eb2b95
Show file tree
Hide file tree
Showing 20 changed files with 1,606 additions and 131 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
15 changes: 12 additions & 3 deletions 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 @@ -55,8 +55,7 @@ class PairingCommand : public CHIPCommand,
PairingCommand(const char * commandName, PairingMode mode, PairingNetworkType networkType,
CredentialIssuerCommands * credIssuerCmds,
chip::Dnssd::DiscoveryFilterType filterType = chip::Dnssd::DiscoveryFilterType::kNone) :
CHIPCommand(commandName, credIssuerCmds),
mPairingMode(mode), mNetworkType(networkType), mFilterType(filterType),
CHIPCommand(commandName, credIssuerCmds), mPairingMode(mode), mNetworkType(networkType), mFilterType(filterType),
mRemoteAddr{ IPAddress::Any, chip::Inet::InterfaceId::Null() }, mComplex_TimeZones(&mTimeZoneList),
mComplex_DSTOffsets(&mDSTOffsetList), mCurrentFabricRemoveCallback(OnCurrentFabricRemove, this)
{
Expand Down Expand Up @@ -182,6 +181,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 +245,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,6 +27,7 @@
#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>
Expand All @@ -35,6 +36,7 @@
#include <platform/ConfigurationManager.h>
#include <platform/DeviceControlServer.h>
#include <tracing/macros.h>
#include <system/SystemConfig.h>

using namespace chip;
using namespace chip::app;
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,56 @@ bool emberAfGeneralCommissioningClusterCommissioningCompleteCallback(
}
else
{
if (failSafe.NocCommandHasBeenInvoked())
CHIP_ERROR err;

#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION
bool hasTermsAndConditionsRequiredAcknowledgementsBeenAccepted;
bool hasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted;

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 = 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 +407,37 @@ 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)
{
#if defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS && defined CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION
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);
#endif
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;
}
default: {
break;
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/app/server/BUILD.gn
Original file line number Diff line number Diff line change
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 2eb2b95

Please sign in to comment.