blob: ce1458cc23e536c3bfbbfb9dee44cf88fe08f997 [file] [log] [blame]
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* Implementation of SetUp Code Pairer, a class that parses a given
* setup code and uses the extracted informations to discover and
* filter commissionables nodes, before initiating the pairing process.
*
*/
#include <controller/SetUpCodePairer.h>
#include <controller/CHIPDeviceController.h>
#include <lib/dnssd/Resolver.h>
#include <lib/support/CodeUtils.h>
#include <memory>
#include <system/SystemClock.h>
#include <tracing/metric_event.h>
#include <vector>
constexpr uint32_t kDeviceDiscoveredTimeout = CHIP_CONFIG_SETUP_CODE_PAIRER_DISCOVERY_TIMEOUT_SECS * chip::kMillisecondsPerSecond;
using namespace chip::Tracing;
namespace chip {
namespace Controller {
CHIP_ERROR SetUpCodePairer::PairDevice(NodeId remoteId, const char * setUpCode, SetupCodePairerBehaviour commission,
DiscoveryType discoveryType, Optional<Dnssd::CommonResolutionData> resolutionData)
{
VerifyOrReturnErrorWithMetric(kMetricSetupCodePairerPairDevice, mSystemLayer != nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnErrorWithMetric(kMetricSetupCodePairerPairDevice, remoteId != kUndefinedNodeId, CHIP_ERROR_INVALID_ARGUMENT);
std::vector<SetupPayload> payloads;
ReturnErrorOnFailure(SetupPayload::FromStringRepresentation(setUpCode, payloads));
// If the caller has provided a specific single resolution data, and we were
// only looking for one commissionee, and the caller says that the provided
// data matches that one commissionee, just go ahead and use the provided data.
//
// If we were looking for more than one device (i.e. if either of the
// payload arrays involved does not have length 1), we can't make use of the
// incoming resolution data, since it does not contain the long
// discriminator of the thing that was discovered, and therefore we can't
// tell which setup passcode to use for it.
if (resolutionData.HasValue() && payloads.size() == 1 && mSetupPayloads.size() == 1)
{
VerifyOrReturnErrorWithMetric(kMetricSetupCodePairerPairDevice, discoveryType != DiscoveryType::kAll,
CHIP_ERROR_INVALID_ARGUMENT);
if (mRemoteId == remoteId && mSetupPayloads[0].setUpPINCode == payloads[0].setUpPINCode && mConnectionType == commission &&
mDiscoveryType == discoveryType)
{
// Not passing a discriminator is ok, since we have only one payload.
NotifyCommissionableDeviceDiscovered(resolutionData.Value(), /* matchedLongDiscriminator = */ std::nullopt);
return CHIP_NO_ERROR;
}
}
ResetDiscoveryState();
mConnectionType = commission;
mDiscoveryType = discoveryType;
mRemoteId = remoteId;
mSetupPayloads = std::move(payloads);
if (resolutionData.HasValue() && mSetupPayloads.size() == 1)
{
// No need to pass in a discriminator if we have only one payload, which
// is good because we don't have a full discriminator here anyway.
NotifyCommissionableDeviceDiscovered(resolutionData.Value(), /* matchedLongDiscriminator = */ std::nullopt);
return CHIP_NO_ERROR;
}
ReturnErrorOnFailureWithMetric(kMetricSetupCodePairerPairDevice, Connect());
auto errorCode =
mSystemLayer->StartTimer(System::Clock::Milliseconds32(kDeviceDiscoveredTimeout), OnDeviceDiscoveredTimeoutCallback, this);
if (CHIP_NO_ERROR == errorCode)
{
MATTER_LOG_METRIC_BEGIN(kMetricSetupCodePairerPairDevice);
}
return errorCode;
}
CHIP_ERROR SetUpCodePairer::Connect()
{
if (mDiscoveryType == DiscoveryType::kAll)
{
if (ShouldDiscoverUsing(RendezvousInformationFlag::kBLE))
{
CHIP_ERROR err = StartDiscoveryOverBLE();
if ((CHIP_ERROR_NOT_IMPLEMENTED == err) || (CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE == err))
{
ChipLogProgress(Controller,
"Skipping commissionable node discovery over BLE since not supported by the controller!");
}
else if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to start commissionable node discovery over BLE: %" CHIP_ERROR_FORMAT,
err.Format());
}
}
if (ShouldDiscoverUsing(RendezvousInformationFlag::kWiFiPAF))
{
CHIP_ERROR err = StartDiscoveryOverWiFiPAF();
if ((CHIP_ERROR_NOT_IMPLEMENTED == err) || (CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE == err))
{
ChipLogProgress(Controller,
"Skipping commissionable node discovery over Wi-Fi PAF since not supported by the controller!");
}
else if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to start commissionable node discovery over Wi-Fi PAF: %" CHIP_ERROR_FORMAT,
err.Format());
}
}
}
// We always want to search on network because any node that has already been commissioned will use on-network regardless of the
// QR code flag.
CHIP_ERROR err = StartDiscoveryOverDNSSD();
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to start commissionable node discovery over DNS-SD: %" CHIP_ERROR_FORMAT, err.Format());
}
return err;
}
CHIP_ERROR SetUpCodePairer::StartDiscoveryOverBLE()
{
#if CONFIG_NETWORK_LAYER_BLE
#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
VerifyOrReturnError(mCommissioner != nullptr, CHIP_ERROR_INCORRECT_STATE);
mCommissioner->ConnectBleTransportToSelf();
#endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
VerifyOrReturnError(mBleLayer != nullptr, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
ChipLogProgress(Controller, "Starting commissionable node discovery over BLE");
// Handle possibly-sync callbacks.
mWaitingForDiscovery[kBLETransport] = true;
CHIP_ERROR err;
// Not all BLE backends support the new NewBleConnectionByDiscriminators
// API, so use the old one when we can (i.e. when we only have one setup
// payload), to avoid breaking existing API consumers.
if (mSetupPayloads.size() == 1)
{
err = mBleLayer->NewBleConnectionByDiscriminator(mSetupPayloads[0].discriminator, this, OnDiscoveredDeviceOverBleSuccess,
OnDiscoveredDeviceOverBleError);
}
else
{
std::vector<SetupDiscriminator> discriminators;
discriminators.reserve(mSetupPayloads.size());
for (auto & payload : mSetupPayloads)
{
discriminators.emplace_back(payload.discriminator);
}
err = mBleLayer->NewBleConnectionByDiscriminators(Span(discriminators.data(), discriminators.size()), this,
OnDiscoveredDeviceWithDiscriminatorOverBleSuccess,
OnDiscoveredDeviceOverBleError);
}
if (err != CHIP_NO_ERROR)
{
mWaitingForDiscovery[kBLETransport] = false;
}
return err;
#else
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#endif // CONFIG_NETWORK_LAYER_BLE
}
CHIP_ERROR SetUpCodePairer::StopDiscoveryOverBLE()
{
// Make sure to not call CancelBleIncompleteConnection unless we are in fact
// waiting on BLE discovery. It will cancel connections that are in fact
// completed. In particular, if we just established PASE over BLE calling
// CancelBleIncompleteConnection here unconditionally would cancel the BLE
// connection underlying the PASE session. So make sure to only call
// CancelBleIncompleteConnection if we're still waiting to hear back on the
// BLE discovery bits.
if (!mWaitingForDiscovery[kBLETransport])
{
return CHIP_NO_ERROR;
}
mWaitingForDiscovery[kBLETransport] = false;
#if CONFIG_NETWORK_LAYER_BLE
VerifyOrReturnError(mBleLayer != nullptr, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
ChipLogProgress(Controller, "Stopping commissionable node discovery over BLE");
return mBleLayer->CancelBleIncompleteConnection();
#else
return CHIP_NO_ERROR;
#endif // CONFIG_NETWORK_LAYER_BLE
}
CHIP_ERROR SetUpCodePairer::StartDiscoveryOverDNSSD()
{
ChipLogProgress(Controller, "Starting commissionable node discovery over DNS-SD");
Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kNone);
if (mSetupPayloads.size() == 1)
{
auto & discriminator = mSetupPayloads[0].discriminator;
if (discriminator.IsShortDiscriminator())
{
filter.type = Dnssd::DiscoveryFilterType::kShortDiscriminator;
filter.code = discriminator.GetShortValue();
}
else
{
filter.type = Dnssd::DiscoveryFilterType::kLongDiscriminator;
filter.code = discriminator.GetLongValue();
}
}
// In theory we could try to filter on the vendor ID if it's the same across all the setup
// payloads, but DNS-SD advertisements are not required to include the Vendor ID subtype, so in
// practice that's not doable.
// Handle possibly-sync callbacks.
mWaitingForDiscovery[kIPTransport] = true;
CHIP_ERROR err = mCommissioner->DiscoverCommissionableNodes(filter);
if (err != CHIP_NO_ERROR)
{
mWaitingForDiscovery[kIPTransport] = false;
}
return err;
}
CHIP_ERROR SetUpCodePairer::StopDiscoveryOverDNSSD()
{
ChipLogProgress(Controller, "Stopping commissionable node discovery over DNS-SD");
mWaitingForDiscovery[kIPTransport] = false;
mCommissioner->StopCommissionableDiscovery();
return CHIP_NO_ERROR;
}
CHIP_ERROR SetUpCodePairer::StartDiscoveryOverWiFiPAF()
{
#if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
if (mSetupPayloads.size() != 1)
{
ChipLogError(Controller, "Wi-Fi PAF commissioning does not support concatenated QR codes yet.");
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
}
auto & payload = mSetupPayloads[0];
ChipLogProgress(Controller, "Starting commissionable node discovery over Wi-Fi PAF");
VerifyOrReturnError(mCommissioner != nullptr, CHIP_ERROR_INCORRECT_STATE);
const SetupDiscriminator connDiscriminator(payload.discriminator);
VerifyOrReturnValue(!connDiscriminator.IsShortDiscriminator(), CHIP_ERROR_INVALID_ARGUMENT,
ChipLogError(Controller, "Error, Long discriminator is required"));
uint16_t discriminator = connDiscriminator.GetLongValue();
WiFiPAF::WiFiPAFSession sessionInfo = { .role = WiFiPAF::WiFiPafRole::kWiFiPafRole_Subscriber,
.nodeId = mRemoteId,
.discriminator = discriminator };
ReturnErrorOnFailure(
DeviceLayer::ConnectivityMgr().GetWiFiPAF()->AddPafSession(WiFiPAF::PafInfoAccess::kAccNodeInfo, sessionInfo));
mWaitingForDiscovery[kWiFiPAFTransport] = true;
CHIP_ERROR err = DeviceLayer::ConnectivityMgr().WiFiPAFSubscribe(discriminator, (void *) this, OnWiFiPAFSubscribeComplete,
OnWiFiPAFSubscribeError);
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Commissionable node discovery over Wi-Fi PAF failed, err = %" CHIP_ERROR_FORMAT, err.Format());
mWaitingForDiscovery[kWiFiPAFTransport] = false;
}
return err;
#else
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
}
CHIP_ERROR SetUpCodePairer::StopDiscoveryOverWiFiPAF()
{
mWaitingForDiscovery[kWiFiPAFTransport] = false;
#if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
DeviceLayer::ConnectivityMgr().WiFiPAFCancelIncompleteSubscribe();
#endif
return CHIP_NO_ERROR;
}
bool SetUpCodePairer::ConnectToDiscoveredDevice()
{
if (mWaitingForPASE)
{
// Nothing to do. Just wait until we either succeed or fail at that
// PASE session establishment.
return false;
}
while (!mDiscoveredParameters.empty())
{
// Grab the first element from the queue and try connecting to it.
// Remove it from the queue before we try to connect, in case the
// connection attempt fails and calls right back into us to try the next
// thing.
SetUpCodePairerParameters params(mDiscoveredParameters.front());
mDiscoveredParameters.pop_front();
if (params.mLongDiscriminator)
{
auto longDiscriminator = *params.mLongDiscriminator;
// Look for a matching setup passcode.
bool found = false;
for (auto & payload : mSetupPayloads)
{
if (payload.discriminator.MatchesLongDiscriminator(longDiscriminator))
{
params.SetSetupPINCode(payload.setUpPINCode);
found = true;
break;
}
}
if (!found)
{
ChipLogError(Controller, "SetUpCodePairer: Discovered discriminator %u does not match any of our setup payloads",
longDiscriminator);
// Move on to the the next discovered params; nothing we can do here.
continue;
}
}
else
{
// No discriminator known for this discovered device. This can work if we have only one
// setup payload, but otherwise we have no idea what setup passcode to use for it.
if (mSetupPayloads.size() == 1)
{
params.SetSetupPINCode(mSetupPayloads[0].setUpPINCode);
}
else
{
ChipLogError(Controller,
"SetUpCodePairer: Unable to handle discovered parameters with no discriminator, because it has %u "
"possible payloads",
static_cast<unsigned>(mSetupPayloads.size()));
continue;
}
}
#if CHIP_PROGRESS_LOGGING
char buf[Transport::PeerAddress::kMaxToStringSize];
params.GetPeerAddress().ToString(buf);
ChipLogProgress(Controller, "Attempting PASE connection to %s", buf);
#endif // CHIP_PROGRESS_LOGGING
// Handle possibly-sync call backs from attempts to establish PASE.
ExpectPASEEstablishment();
if (params.GetPeerAddress().GetTransportType() == Transport::Type::kUdp)
{
mCurrentPASEParameters.SetValue(params);
}
CHIP_ERROR err;
if (mConnectionType == SetupCodePairerBehaviour::kCommission)
{
err = mCommissioner->PairDevice(mRemoteId, params);
}
else
{
err = mCommissioner->EstablishPASEConnection(mRemoteId, params);
}
LogErrorOnFailure(err);
if (err == CHIP_NO_ERROR)
{
return true;
}
// Failed to start establishing PASE. Move on to the next item.
PASEEstablishmentComplete();
}
return false;
}
#if CONFIG_NETWORK_LAYER_BLE
void SetUpCodePairer::OnDiscoveredDeviceOverBle(BLE_CONNECTION_OBJECT connObj, std::optional<uint16_t> matchedLongDiscriminator)
{
ChipLogProgress(Controller, "Discovered device to be commissioned over BLE");
mWaitingForDiscovery[kBLETransport] = false;
// In order to not wait for all the possible addresses discovered over mdns to
// be tried before trying to connect over BLE, the discovered connection object is
// inserted at the beginning of the list.
//
// It makes it the 'next' thing to try to connect to if there are already some
// discovered parameters in the list.
//
// TODO: Consider implementing the SHOULD the spec has about commissioning things
// in QR code order by waiting for a second or something before actually starting
// the first PASE session when we have multiple setup payloads, and sorting the
// results in setup payload order. If we do this, we might want to restrict it to
// cases when the different payloads have different vendor/product IDs, since if
// they are all the same product presumably ordering really does not matter.
mDiscoveredParameters.emplace_front(connObj, matchedLongDiscriminator);
ConnectToDiscoveredDevice();
}
void SetUpCodePairer::OnDiscoveredDeviceOverBleSuccess(void * appState, BLE_CONNECTION_OBJECT connObj)
{
(static_cast<SetUpCodePairer *>(appState))->OnDiscoveredDeviceOverBle(connObj, std::nullopt);
}
void SetUpCodePairer::OnDiscoveredDeviceWithDiscriminatorOverBleSuccess(void * appState, uint16_t matchedLongDiscriminator,
BLE_CONNECTION_OBJECT connObj)
{
(static_cast<SetUpCodePairer *>(appState))->OnDiscoveredDeviceOverBle(connObj, std::make_optional(matchedLongDiscriminator));
}
void SetUpCodePairer::OnDiscoveredDeviceOverBleError(void * appState, CHIP_ERROR err)
{
static_cast<SetUpCodePairer *>(appState)->OnBLEDiscoveryError(err);
}
void SetUpCodePairer::OnBLEDiscoveryError(CHIP_ERROR err)
{
ChipLogError(Controller, "Commissionable node discovery over BLE failed: %" CHIP_ERROR_FORMAT, err.Format());
mWaitingForDiscovery[kBLETransport] = false;
LogErrorOnFailure(err);
}
#endif // CONFIG_NETWORK_LAYER_BLE
#if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
void SetUpCodePairer::OnDiscoveredDeviceOverWifiPAF()
{
ChipLogProgress(Controller, "Discovered device to be commissioned over Wi-Fi PAF, RemoteId: %lu", mRemoteId);
mWaitingForDiscovery[kWiFiPAFTransport] = false;
auto param = SetUpCodePairerParameters();
param.SetPeerAddress(Transport::PeerAddress(Transport::Type::kWiFiPAF, mRemoteId));
// TODO: This needs to support concatenated QR codes and set the relevant
// long discriminator on param.
//
// See https://github.com/project-chip/connectedhomeip/issues/39134
mDiscoveredParameters.emplace_back(param);
ConnectToDiscoveredDevice();
}
void SetUpCodePairer::OnWifiPAFDiscoveryError(CHIP_ERROR err)
{
ChipLogError(Controller, "Commissionable node discovery over Wi-Fi PAF failed: %" CHIP_ERROR_FORMAT, err.Format());
mWaitingForDiscovery[kWiFiPAFTransport] = false;
}
void SetUpCodePairer::OnWiFiPAFSubscribeComplete(void * appState)
{
auto self = reinterpret_cast<SetUpCodePairer *>(appState);
self->OnDiscoveredDeviceOverWifiPAF();
}
void SetUpCodePairer::OnWiFiPAFSubscribeError(void * appState, CHIP_ERROR err)
{
auto self = reinterpret_cast<SetUpCodePairer *>(appState);
self->OnWifiPAFDiscoveryError(err);
}
#endif
bool SetUpCodePairer::IdIsPresent(uint16_t vendorOrProductID)
{
return vendorOrProductID != kNotAvailable;
}
bool SetUpCodePairer::NodeMatchesCurrentFilter(const Dnssd::DiscoveredNodeData & discNodeData) const
{
if (!discNodeData.Is<Dnssd::CommissionNodeData>())
{
return false;
}
const Dnssd::CommissionNodeData & nodeData = discNodeData.Get<Dnssd::CommissionNodeData>();
if (nodeData.commissioningMode == 0)
{
ChipLogProgress(Controller, "Discovered device does not have an open commissioning window.");
return false;
}
// Check whether this matches one of our setup payloads.
for (auto & payload : mSetupPayloads)
{
// The advertisement may not include a vendor id, and the payload may not have one either.
if (IdIsPresent(payload.vendorID) && IdIsPresent(nodeData.vendorId) && payload.vendorID != nodeData.vendorId)
{
ChipLogProgress(Controller, "Discovered device vendor ID (%u) does not match our vendor ID (%u).", nodeData.vendorId,
payload.vendorID);
continue;
}
// The advertisement may not include a product id, and the payload may not have one either.
if (IdIsPresent(payload.productID) && IdIsPresent(nodeData.productId) && payload.productID != nodeData.productId)
{
ChipLogProgress(Controller, "Discovered device product ID (%u) does not match our product ID (%u).", nodeData.productId,
payload.productID);
continue;
}
if (!payload.discriminator.MatchesLongDiscriminator(nodeData.longDiscriminator))
{
ChipLogProgress(Controller, "Discovered device discriminator (%u) does not match our discriminator.",
nodeData.longDiscriminator);
continue;
}
ChipLogProgress(Controller, "Discovered device with discriminator %u matches one of our setup payloads",
nodeData.longDiscriminator);
return true;
}
return false;
}
void SetUpCodePairer::NotifyCommissionableDeviceDiscovered(const Dnssd::DiscoveredNodeData & nodeData)
{
if (!NodeMatchesCurrentFilter(nodeData))
{
return;
}
ChipLogProgress(Controller, "Discovered device to be commissioned over DNS-SD");
auto & commissionableNodeData = nodeData.Get<Dnssd::CommissionNodeData>();
NotifyCommissionableDeviceDiscovered(commissionableNodeData, std::make_optional(commissionableNodeData.longDiscriminator));
}
void SetUpCodePairer::NotifyCommissionableDeviceDiscovered(const Dnssd::CommonResolutionData & resolutionData,
std::optional<uint16_t> matchedLongDiscriminator)
{
if (mDiscoveryType == DiscoveryType::kDiscoveryNetworkOnlyWithoutPASEAutoRetry)
{
// If the discovery type does not want the PASE auto retry mechanism, we will just store
// a single IP. So the discovery process is stopped as it won't be of any help anymore.
StopDiscoveryOverDNSSD();
mDiscoveredParameters.emplace_back(resolutionData, matchedLongDiscriminator, 0);
}
else
{
for (size_t i = 0; i < resolutionData.numIPs; i++)
{
mDiscoveredParameters.emplace_back(resolutionData, matchedLongDiscriminator, i);
}
}
ConnectToDiscoveredDevice();
}
bool SetUpCodePairer::StopPairing(NodeId remoteId)
{
VerifyOrReturnValue(mRemoteId != kUndefinedNodeId, false);
VerifyOrReturnValue(remoteId == kUndefinedNodeId || remoteId == mRemoteId, false);
if (mWaitingForPASE)
{
PASEEstablishmentComplete();
}
ResetDiscoveryState();
mRemoteId = kUndefinedNodeId;
return true;
}
bool SetUpCodePairer::TryNextRendezvousParameters()
{
if (ConnectToDiscoveredDevice())
{
ChipLogProgress(Controller, "Trying connection to commissionee over different transport");
return true;
}
if (DiscoveryInProgress())
{
ChipLogProgress(Controller, "Waiting to discover commissionees that match our filters");
return true;
}
return false;
}
bool SetUpCodePairer::DiscoveryInProgress() const
{
for (const auto & waiting : mWaitingForDiscovery)
{
if (waiting)
{
return true;
}
}
return false;
}
void SetUpCodePairer::StopAllDiscoveryAttempts()
{
LogErrorOnFailure(StopDiscoveryOverBLE());
LogErrorOnFailure(StopDiscoveryOverDNSSD());
LogErrorOnFailure(StopDiscoveryOverWiFiPAF());
// Just in case any of those failed to reset the waiting state properly.
for (auto & waiting : mWaitingForDiscovery)
{
waiting = false;
}
}
void SetUpCodePairer::ResetDiscoveryState()
{
StopAllDiscoveryAttempts();
mDiscoveredParameters.clear();
mCurrentPASEParameters.ClearValue();
mLastPASEError = CHIP_NO_ERROR;
mSetupPayloads.clear();
mSystemLayer->CancelTimer(OnDeviceDiscoveredTimeoutCallback, this);
}
void SetUpCodePairer::ExpectPASEEstablishment()
{
VerifyOrDie(!mWaitingForPASE);
mWaitingForPASE = true;
auto * delegate = mCommissioner->GetPairingDelegate();
VerifyOrDie(delegate != this);
mPairingDelegate = delegate;
mCommissioner->RegisterPairingDelegate(this);
}
void SetUpCodePairer::PASEEstablishmentComplete()
{
VerifyOrDie(mWaitingForPASE);
mWaitingForPASE = false;
mCommissioner->RegisterPairingDelegate(mPairingDelegate);
mPairingDelegate = nullptr;
}
void SetUpCodePairer::OnStatusUpdate(DevicePairingDelegate::Status status)
{
if (status == DevicePairingDelegate::Status::SecurePairingFailed)
{
// If we're still waiting on discovery, don't propagate this failure
// (which is due to PASE failure with something we discovered, but the
// "something" may not have been the right thing) for now. Wait until
// discovery completes. Then we will either succeed and notify
// accordingly or time out and land in OnStatusUpdate again, but at that
// point we will not be waiting on discovery anymore.
if (!mDiscoveredParameters.empty())
{
ChipLogProgress(Controller, "Ignoring SecurePairingFailed status for now; we have more discovered devices to try");
return;
}
if (DiscoveryInProgress())
{
ChipLogProgress(Controller,
"Ignoring SecurePairingFailed status for now; we are waiting to see if we discover more devices");
return;
}
}
if (mPairingDelegate)
{
mPairingDelegate->OnStatusUpdate(status);
}
}
void SetUpCodePairer::OnPairingComplete(CHIP_ERROR error)
{
// Save the pairing delegate so we can notify it. We want to notify it
// _after_ we restore the state on the commissioner, in case the delegate
// ends up immediately calling back into the commissioner again when
// notified.
auto * pairingDelegate = mPairingDelegate;
PASEEstablishmentComplete();
if (CHIP_NO_ERROR == error)
{
ChipLogProgress(Controller, "PASE session established with commissionee. Stopping discovery.");
ResetDiscoveryState();
mRemoteId = kUndefinedNodeId;
MATTER_LOG_METRIC_END(kMetricSetupCodePairerPairDevice, error);
if (pairingDelegate != nullptr)
{
pairingDelegate->OnPairingComplete(error);
}
return;
}
// It may happen that there is a stale DNS entry. If so, ReconfirmRecord will flush
// the record from the daemon cache once it determines that it is invalid.
// It may not help for this particular resolve, but may help subsequent resolves.
if (CHIP_ERROR_TIMEOUT == error && mCurrentPASEParameters.HasValue())
{
const auto & params = mCurrentPASEParameters.Value();
const auto & peer = params.GetPeerAddress();
const auto & ip = peer.GetIPAddress();
auto err = Dnssd::Resolver::Instance().ReconfirmRecord(params.mHostName, ip, params.mInterfaceId);
if (CHIP_NO_ERROR != err && CHIP_ERROR_NOT_IMPLEMENTED != err)
{
ChipLogError(Controller, "Error when verifying the validity of an address: %" CHIP_ERROR_FORMAT, err.Format());
}
}
mCurrentPASEParameters.ClearValue();
// We failed to establish PASE. Try the next thing we have discovered, if
// any.
if (TryNextRendezvousParameters())
{
// Keep waiting until that finishes. Don't call OnPairingComplete yet.
mLastPASEError = error;
return;
}
MATTER_LOG_METRIC_END(kMetricSetupCodePairerPairDevice, error);
if (pairingDelegate != nullptr)
{
pairingDelegate->OnPairingComplete(error);
}
}
void SetUpCodePairer::OnPairingDeleted(CHIP_ERROR error)
{
if (mPairingDelegate)
{
mPairingDelegate->OnPairingDeleted(error);
}
}
void SetUpCodePairer::OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error)
{
// Not really expecting this, but handle it anyway.
if (mPairingDelegate)
{
mPairingDelegate->OnCommissioningComplete(deviceId, error);
}
}
void SetUpCodePairer::OnDeviceDiscoveredTimeoutCallback(System::Layer * layer, void * context)
{
ChipLogError(Controller, "Discovery timed out");
auto * pairer = static_cast<SetUpCodePairer *>(context);
pairer->StopAllDiscoveryAttempts();
if (!pairer->mWaitingForPASE && pairer->mDiscoveredParameters.empty())
{
// We're not waiting on any more PASE attempts, and we're not going to
// discover anything at this point, so we should just notify our
// listener.
CHIP_ERROR err = pairer->mLastPASEError;
if (err == CHIP_NO_ERROR)
{
err = CHIP_ERROR_TIMEOUT;
}
MATTER_LOG_METRIC_END(kMetricSetupCodePairerPairDevice, err);
pairer->mCommissioner->OnSessionEstablishmentError(err);
}
}
bool SetUpCodePairer::ShouldDiscoverUsing(RendezvousInformationFlag commissioningChannel) const
{
for (auto & payload : mSetupPayloads)
{
auto & rendezvousInformation = payload.rendezvousInformation;
if (!rendezvousInformation.HasValue())
{
// No idea which commissioning channels this device supports, so we
// should be trying using all of them.
return true;
}
if (rendezvousInformation.Value().Has(commissioningChannel))
{
return true;
}
}
// None of the payloads claimed support for this commissioning channel.
return false;
}
SetUpCodePairerParameters::SetUpCodePairerParameters(const Dnssd::CommonResolutionData & data,
std::optional<uint16_t> longDiscriminator, size_t index) :
mLongDiscriminator(longDiscriminator)
{
mInterfaceId = data.interfaceId;
Platform::CopyString(mHostName, data.hostName);
auto & ip = data.ipAddress[index];
SetPeerAddress(Transport::PeerAddress::UDP(ip, data.port, ip.IsIPv6LinkLocal() ? data.interfaceId : Inet::InterfaceId::Null()));
if (data.mrpRetryIntervalIdle.has_value())
{
SetIdleInterval(*data.mrpRetryIntervalIdle);
}
if (data.mrpRetryIntervalActive.has_value())
{
SetActiveInterval(*data.mrpRetryIntervalActive);
}
}
#if CONFIG_NETWORK_LAYER_BLE
SetUpCodePairerParameters::SetUpCodePairerParameters(BLE_CONNECTION_OBJECT connObj, std::optional<uint16_t> longDiscriminator,
bool connected) :
mLongDiscriminator(longDiscriminator)
{
Transport::PeerAddress peerAddress = Transport::PeerAddress::BLE();
SetPeerAddress(peerAddress);
if (connected)
{
SetConnectionObject(connObj);
}
else
{
SetDiscoveredObject(connObj);
}
}
#endif // CONFIG_NETWORK_LAYER_BLE
} // namespace Controller
} // namespace chip