Skip to content

Commit

Permalink
Add Commissioning State Machine
Browse files Browse the repository at this point in the history
The commissioning state machine operates without dependence on the
chip::Controller::DeviceCommissioner, CommissioneeDeviceProxy,
CASEClient or CASESessionManager objects.  And using the recently
introduced StateMachine utility in src/lib, the needed flexibility
for ecosystem-specific commissioning flows is available.

The state machine is separated into two primary resources:

* CommissioningStates.h with templates that can support all commissioners
* ExampleCommissioningStateMachine.h, a concrete implementation that
  uses the states and provides an example that apps can copy, modify,
  and extend

App implementations can add, remove and reorder states as necessary.
Arbitrarily complex async UI logic is also easily achieved without the
need to define any callback delegate interfaces.  This implicitly fixes
issues like project-chip#11917 (Refactor DeviceAttestationVerifier to have Async
interface).

ExampleCommissioningStateMachine takes the place of DeviceCommissioner
in chip-tool, providing feature parity.  chip-tool now uses the new code
to commission devices, and on success then places them into a
DeviceController for persistence and subsequent interaction.

Fixes project-chip#7951
  • Loading branch information
msandstedt committed Dec 22, 2021
1 parent ba00049 commit 72b4d37
Show file tree
Hide file tree
Showing 17 changed files with 2,957 additions and 85 deletions.
4 changes: 2 additions & 2 deletions examples/chip-tool/commands/common/CHIPCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ uint16_t CHIPCommand::CurrentCommissionerIndex()
return index;
}

chip::Controller::DeviceCommissioner & CHIPCommand::CurrentCommissioner()
chip::Controller::DeviceController & CHIPCommand::CurrentCommissioner()
{
auto item = mCommissioners.find(GetIdentity());
return *item->second.get();
Expand Down Expand Up @@ -202,7 +202,7 @@ CHIP_ERROR CHIPCommand::InitializeCommissioner(std::string key, chip::FabricId f
commissionerParams.controllerNOC = nocSpan;
commissionerParams.controllerVendorId = chip::VendorId::TestVendor1;

ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().SetupCommissioner(commissionerParams, *(commissioner.get())));
ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().SetupController(commissionerParams, *(commissioner.get())));
mCommissioners[key] = std::move(commissioner);

return CHIP_NO_ERROR;
Expand Down
3 changes: 2 additions & 1 deletion examples/chip-tool/commands/common/CHIPCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#pragma once

#include <controller/ExampleCommissioningStateMachine.h>
#include <controller/ExampleOperationalCredentialsIssuer.h>

#include "../../config/PersistentStorage.h"
Expand All @@ -35,7 +36,7 @@ class CHIPCommand : public Command
{
public:
using ChipDevice = ::chip::DeviceProxy;
using ChipDeviceCommissioner = ::chip::Controller::DeviceCommissioner;
using ChipDeviceCommissioner = ::chip::Controller::DeviceController;
using ChipDeviceController = ::chip::Controller::DeviceController;
using IPAddress = ::chip::Inet::IPAddress;
using NodeId = ::chip::NodeId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,26 @@
*/

#include "DiscoverCommissionablesCommand.h"
#include <controller/CHIPDeviceControllerFactory.h>
#include <lib/support/BytesToHex.h>

using namespace ::chip;

CHIP_ERROR DiscoverCommissionablesCommand::RunCommand()
{
CurrentCommissioner().RegisterDeviceDiscoveryDelegate(this);
ReturnErrorOnFailure(
mDnsResolver.Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState()->UDPEndPointManager()));
mDnsResolver.SetResolverDelegate(this);
Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kNone, (uint64_t) 0);
return CurrentCommissioner().DiscoverCommissionableNodes(filter);
ReturnErrorOnFailure(mDnsResolver.FindCommissionableNodes(filter));
return CHIP_NO_ERROR;
}

void DiscoverCommissionablesCommand::OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData)
void DiscoverCommissionablesCommand::OnNodeDiscoveryComplete(const chip::Dnssd::DiscoveredNodeData & nodeData)
{
nodeData.LogDetail();
}

void DiscoverCommissionablesCommand::OnNodeIdResolved(const chip::Dnssd::ResolvedNodeData & nodeData) {}

void DiscoverCommissionablesCommand::OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) {}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,20 @@

#include "../common/CHIPCommand.h"

class DiscoverCommissionablesCommand : public CHIPCommand, public chip::Controller::DeviceDiscoveryDelegate
class DiscoverCommissionablesCommand : public CHIPCommand, public chip::Dnssd::ResolverDelegate
{
public:
DiscoverCommissionablesCommand() : CHIPCommand("commissionables") {}

/////////// DeviceDiscoveryDelegate Interface /////////
void OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData) override;
/////////// ResolverDelegate Interface /////////
void OnNodeIdResolved(const chip::Dnssd::ResolvedNodeData & nodeData) override;
void OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) override;
void OnNodeDiscoveryComplete(const chip::Dnssd::DiscoveredNodeData & nodeData) override;

/////////// CHIPCommand Interface /////////
CHIP_ERROR RunCommand() override;
chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(30); }

private:
chip::Dnssd::ResolverProxy mDnsResolver;
};
151 changes: 92 additions & 59 deletions examples/chip-tool/commands/pairing/PairingCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "PairingCommand.h"
#include "platform/PlatformManager.h"
#include <controller/CHIPDeviceControllerFactory.h>
#include <controller/ExampleOperationalCredentialsIssuer.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPSafeCasts.h>
Expand All @@ -29,10 +30,10 @@

using namespace ::chip;
using namespace ::chip::Controller;
using Commissionee = chip::Controller::CommissioningStates::Commissionee;

CHIP_ERROR PairingCommand::RunCommand()
{
CurrentCommissioner().RegisterPairingDelegate(this);
return RunInternal(mNodeId);
}

Expand Down Expand Up @@ -85,20 +86,66 @@ CommissioningParameters PairingCommand::GetCommissioningParameters()

CHIP_ERROR PairingCommand::PairWithQRCode(NodeId remoteId)
{
return CurrentCommissioner().PairDevice(remoteId, mOnboardingPayload);
using StateMachine = chip::Controller::ExampleCommissioningStateMachine::ExampleCommissioningStateMachine;
auto stateMachine = Platform::MakeShared<StateMachine>();
VerifyOrReturnError(stateMachine.get() != nullptr, CHIP_ERROR_NO_MEMORY);
auto onSuccess = [this, stateMachine](chip::Controller::CommissioningStates::Commissionee & commissionee) {
OnCommissioningComplete(*stateMachine.get());
};
auto onFailure = [this, stateMachine](chip::Controller::CommissioningStates::Commissionee & commissionee) {
OnCommissioningFailure(*stateMachine.get());
};
stateMachine.get()->Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState(), &mOpCredsIssuer,
CurrentCommissioner().GetFabricIndex(), mNodeId, mOperationalDataset, mSSID, mPassword);
CHIP_ERROR err = stateMachine.get()->Commission(mOnboardingPayload, onSuccess, onFailure);
if (err != CHIP_NO_ERROR)
{
stateMachine.get()->Shutdown();
}
return err;
}

CHIP_ERROR PairingCommand::PairWithManualCode(NodeId remoteId)
{
return CurrentCommissioner().PairDevice(remoteId, mOnboardingPayload);
using StateMachine = chip::Controller::ExampleCommissioningStateMachine::ExampleCommissioningStateMachine;
auto stateMachine = Platform::MakeShared<StateMachine>();
VerifyOrReturnError(stateMachine.get() != nullptr, CHIP_ERROR_NO_MEMORY);
auto onSuccess = [this, stateMachine](chip::Controller::CommissioningStates::Commissionee & commissionee) {
OnCommissioningComplete(*stateMachine.get());
};
auto onFailure = [this, stateMachine](chip::Controller::CommissioningStates::Commissionee & commissionee) {
OnCommissioningFailure(*stateMachine.get());
};
stateMachine.get()->Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState(), &mOpCredsIssuer,
CurrentCommissioner().GetFabricIndex(), mNodeId, mOperationalDataset, mSSID, mPassword);
CHIP_ERROR err = stateMachine.get()->Commission(mOnboardingPayload, onSuccess, onFailure);
if (err != CHIP_NO_ERROR)
{
stateMachine.get()->Shutdown();
}
return err;
}

CHIP_ERROR PairingCommand::Pair(NodeId remoteId, PeerAddress address)
{
RendezvousParameters params =
RendezvousParameters().SetSetupPINCode(mSetupPINCode).SetDiscriminator(mDiscriminator).SetPeerAddress(address);
CommissioningParameters commissioningParams = GetCommissioningParameters();
return CurrentCommissioner().PairDevice(remoteId, params, commissioningParams);
using StateMachine = chip::Controller::ExampleCommissioningStateMachine::ExampleCommissioningStateMachine;
auto stateMachine = Platform::MakeShared<StateMachine>();
VerifyOrReturnError(stateMachine.get() != nullptr, CHIP_ERROR_NO_MEMORY);
auto onSuccess = [this, stateMachine](chip::Controller::CommissioningStates::Commissionee & commissionee) {
OnCommissioningComplete(*stateMachine.get());
};
auto onFailure = [this, stateMachine](chip::Controller::CommissioningStates::Commissionee & commissionee) {
OnCommissioningFailure(*stateMachine.get());
};
stateMachine.get()->Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState(), &mOpCredsIssuer,
CurrentCommissioner().GetFabricIndex(), mNodeId, mOperationalDataset, mSSID, mPassword);
CHIP_ERROR err =
stateMachine.get()->Commission(chip::RendezvousInformationFlag::kNone, mDiscriminator, mSetupPINCode, onSuccess, onFailure);
if (err != CHIP_NO_ERROR)
{
stateMachine.get()->Shutdown();
}
return err;
}

CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId)
Expand All @@ -109,7 +156,23 @@ CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId)
case chip::Dnssd::DiscoveryFilterType::kNone:
break;
case chip::Dnssd::DiscoveryFilterType::kShortDiscriminator:
case chip::Dnssd::DiscoveryFilterType::kLongDiscriminator:
case chip::Dnssd::DiscoveryFilterType::kLongDiscriminator: {
using StateMachine = chip::Controller::ExampleCommissioningStateMachine::ExampleCommissioningStateMachine;
auto stateMachine = Platform::MakeShared<StateMachine>();
VerifyOrReturnError(stateMachine.get() != nullptr, CHIP_ERROR_NO_MEMORY);
auto onSuccess = [this, stateMachine](chip::Controller::CommissioningStates::Commissionee & commissionee) {
OnCommissioningComplete(*stateMachine.get());
};
auto onFailure = [this, stateMachine](chip::Controller::CommissioningStates::Commissionee & commissionee) {
OnCommissioningFailure(*stateMachine.get());
};
stateMachine.get()->Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState(), &mOpCredsIssuer,
CurrentCommissioner().GetFabricIndex(), mNodeId, mOperationalDataset, mSSID, mPassword);
CHIP_ERROR err =
stateMachine.get()->Commission(chip::RendezvousInformationFlag::kOnNetwork, static_cast<uint16_t>(mDiscoveryFilterCode),
mSetupPINCode, onSuccess, onFailure);
return err;
}
case chip::Dnssd::DiscoveryFilterType::kCompressedFabricId:
case chip::Dnssd::DiscoveryFilterType::kVendorId:
case chip::Dnssd::DiscoveryFilterType::kDeviceType:
Expand All @@ -126,84 +189,50 @@ CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId)
break;
}

CurrentCommissioner().RegisterDeviceDiscoveryDelegate(this);
return CurrentCommissioner().DiscoverCommissionableNodes(filter);
ReturnErrorOnFailure(
mDnsResolver.Init(chip::Controller::DeviceControllerFactory::GetInstance().GetSystemState()->UDPEndPointManager()));
mDnsResolver.SetResolverDelegate(this);
ReturnErrorOnFailure(mDnsResolver.FindCommissionableNodes(filter));
return CHIP_NO_ERROR;
}

CHIP_ERROR PairingCommand::Unpair(NodeId remoteId)
{
CHIP_ERROR err = CurrentCommissioner().UnpairDevice(remoteId);
CurrentCommissioner().ReleaseOperationalDevice(remoteId);
CHIP_ERROR err = CHIP_NO_ERROR;
SetCommandExitStatus(err);
return err;
}

void PairingCommand::OnStatusUpdate(DevicePairingDelegate::Status status)
{
switch (status)
{
case DevicePairingDelegate::Status::SecurePairingSuccess:
ChipLogProgress(chipTool, "Secure Pairing Success");
break;
case DevicePairingDelegate::Status::SecurePairingFailed:
ChipLogError(chipTool, "Secure Pairing Failed");
break;
}
}

void PairingCommand::OnPairingComplete(CHIP_ERROR err)
{
if (err == CHIP_NO_ERROR)
{
ChipLogProgress(chipTool, "Pairing Success");
}
else
{
ChipLogProgress(chipTool, "Pairing Failure: %s", ErrorStr(err));
}

if (err != CHIP_NO_ERROR)
{
SetCommandExitStatus(err);
}
}

void PairingCommand::OnPairingDeleted(CHIP_ERROR err)
void PairingCommand::OnCommissioningComplete(CommissioningStateMachine & stateMachine)
{
CHIP_ERROR err = stateMachine.GrabCommissionee(CurrentCommissioner());
if (err == CHIP_NO_ERROR)
{
ChipLogProgress(chipTool, "Pairing Deleted Success");
ChipLogProgress(chipTool, "Device commissioning completed with success");
}
else
{
ChipLogProgress(chipTool, "Pairing Deleted Failure: %s", ErrorStr(err));
ChipLogProgress(chipTool, "Device commissioning Failure: %s", ErrorStr(err));
}

stateMachine.Shutdown();
SetCommandExitStatus(err);
}

void PairingCommand::OnCommissioningComplete(NodeId nodeId, CHIP_ERROR err)
void PairingCommand::OnCommissioningFailure(CommissioningStateMachine & stateMachine)
{
if (err == CHIP_NO_ERROR)
{
ChipLogProgress(chipTool, "Device commissioning completed with success");
}
else
{
ChipLogProgress(chipTool, "Device commissioning Failure: %s", ErrorStr(err));
}

SetCommandExitStatus(err);
ChipLogProgress(chipTool, "Device commissioning Failure");
SetCommandExitStatus(CHIP_ERROR_INTERNAL);
}

void PairingCommand::OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData)
void PairingCommand::OnNodeDiscoveryComplete(const chip::Dnssd::DiscoveredNodeData & nodeData)
{
const uint16_t port = nodeData.port;
char buf[chip::Inet::IPAddress::kMaxStringLength];
nodeData.ipAddress[0].ToString(buf);
ChipLogProgress(chipTool, "Discovered Device: %s:%u", buf, port);

// Stop Mdns discovery. Is it the right method ?
CurrentCommissioner().RegisterDeviceDiscoveryDelegate(nullptr);
mDnsResolver.Shutdown();

Inet::InterfaceId interfaceId = nodeData.ipAddress[0].IsIPv6LinkLocal() ? nodeData.interfaceId[0] : Inet::InterfaceId::Null();
PeerAddress peerAddress = PeerAddress::UDP(nodeData.ipAddress[0], port, interfaceId);
Expand All @@ -213,3 +242,7 @@ void PairingCommand::OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData &
SetCommandExitStatus(err);
}
}

void PairingCommand::OnNodeIdResolved(const chip::Dnssd::ResolvedNodeData & nodeData) {}

void PairingCommand::OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) {}
21 changes: 10 additions & 11 deletions examples/chip-tool/commands/pairing/PairingCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ enum class PairingNetworkType
Ethernet,
};

class PairingCommand : public CHIPCommand,
public chip::Controller::DevicePairingDelegate,
public chip::Controller::DeviceDiscoveryDelegate
class PairingCommand : public CHIPCommand, public chip::Dnssd::ResolverDelegate
{
using CommissioningStateMachine = chip::Controller::ExampleCommissioningStateMachine::ExampleCommissioningStateMachine;

public:
PairingCommand(const char * commandName, PairingMode mode, PairingNetworkType networkType,
chip::Dnssd::DiscoveryFilterType filterType = chip::Dnssd::DiscoveryFilterType::kNone) :
Expand Down Expand Up @@ -133,14 +133,10 @@ class PairingCommand : public CHIPCommand,
CHIP_ERROR RunCommand() override;
chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(120); }

/////////// DevicePairingDelegate Interface /////////
void OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status) override;
void OnPairingComplete(CHIP_ERROR error) override;
void OnPairingDeleted(CHIP_ERROR error) override;
void OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error) override;

/////////// DeviceDiscoveryDelegate Interface /////////
void OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData) override;
/////////// ResolverDelegate Interface /////////
void OnNodeIdResolved(const chip::Dnssd::ResolvedNodeData & nodeData) override;
void OnNodeIdResolutionFailed(const PeerId & peerId, CHIP_ERROR error) override;
void OnNodeDiscoveryComplete(const chip::Dnssd::DiscoveredNodeData & nodeData) override;

private:
CHIP_ERROR RunInternal(NodeId remoteId);
Expand All @@ -151,6 +147,8 @@ class PairingCommand : public CHIPCommand,
CHIP_ERROR PairWithCode(NodeId remoteId, chip::SetupPayload payload);
CHIP_ERROR Unpair(NodeId remoteId);
chip::Controller::CommissioningParameters GetCommissioningParameters();
void OnCommissioningComplete(CommissioningStateMachine & stateMachine);
void OnCommissioningFailure(CommissioningStateMachine & stateMachine);

const PairingMode mPairingMode;
const PairingNetworkType mNetworkType;
Expand All @@ -166,6 +164,7 @@ class PairingCommand : public CHIPCommand,
char * mOnboardingPayload;
uint64_t mDiscoveryFilterCode;
char * mDiscoveryFilterInstanceName;
chip::Dnssd::ResolverProxy mDnsResolver;

chip::CommissioneeDeviceProxy * mDevice;
chip::EndpointId mEndpointId = 0;
Expand Down
3 changes: 3 additions & 0 deletions src/controller/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ static_library("controller") {
"CHIPDeviceControllerFactory.h",
"CommissioneeDeviceProxy.cpp",
"CommissioneeDeviceProxy.h",
"CommissioningStates.h",
"DeviceAddressUpdateDelegate.h",
"DeviceDiscoveryDelegate.h",
"EmptyDataModelHandler.cpp",
"ExampleCommissioningStateMachine.cpp",
"ExampleCommissioningStateMachine.h",
"ExampleOperationalCredentialsIssuer.cpp",
"ExampleOperationalCredentialsIssuer.h",
"SetUpCodePairer.cpp",
Expand Down
Loading

0 comments on commit 72b4d37

Please sign in to comment.