Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add network retry and case failsafe timer to AutoCommissioner #23209

Merged
merged 5 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions src/controller/AutoCommissioner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio
(mParams.GetAttemptThreadNetworkScan().ValueOr(false) &&
mDeviceCommissioningInfo.network.thread.endpoint != kInvalidEndpointId))
{
// Perform Scan (kScanNetworks) and collect credentials (kNeedsNetworkCreds) right before configuring network.
// This order of steps allows the workflow to return to collect credentials again if network enablement fails.
return CommissioningStage::kScanNetworks;
}
ChipLogProgress(Controller, "No NetworkScan enabled or WiFi/Thread endpoint not specified, skipping ScanNetworks");
Expand All @@ -260,7 +262,7 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio
}
else
{
finalizePASESession();
setCASEFailsafeTimerIfNeeded();
if (mParams.GetSkipCommissioningComplete().ValueOr(false))
{
return CommissioningStage::kCleanup;
Expand Down Expand Up @@ -317,16 +319,16 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio
}
else if (mParams.GetSkipCommissioningComplete().ValueOr(false))
{
finalizePASESession();
setCASEFailsafeTimerIfNeeded();
return CommissioningStage::kCleanup;
}
else
{
finalizePASESession();
setCASEFailsafeTimerIfNeeded();
return CommissioningStage::kFindOperational;
}
case CommissioningStage::kThreadNetworkEnable:
finalizePASESession();
setCASEFailsafeTimerIfNeeded();
if (mParams.GetSkipCommissioningComplete().ValueOr(false))
{
return CommissioningStage::kCleanup;
Expand All @@ -345,14 +347,33 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio
return CommissioningStage::kError;
}

void AutoCommissioner::finalizePASESession()
// No specific actions to take when an error happens since this command can fail and commissioning can still succeed.
static void OnFailsafeFailureForCASE(void * context, CHIP_ERROR error)
{
ChipLogProgress(Controller, "ExtendFailsafe received failure response %s\n", chip::ErrorStr(error));
}

// No specific actions to take upon success.
static void
OnExtendFailsafeSuccessForCASE(void * context,
const app::Clusters::GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data)
{
ChipLogProgress(Controller, "ExtendFailsafe received ArmFailSafe response errorCode=%u", to_underlying(data.errorCode));
}

void AutoCommissioner::setCASEFailsafeTimerIfNeeded()
{
// if there is a final fail-safe timer configured then, send it
if (mParams.GetCASEFailsafeTimerSeconds().HasValue())
{
// send the command via the PASE session (mCommissioneeDeviceProxy) since the CASE portion of commissioning
// might be done by a different service (ex. PASE is done by a phone app and CASE is done by a Hub).
// Also, we want the CASE failsafe timer to apply for the time it takes the Hub to perform operational discovery,
// CASE establishment, and receipt of the commissioning complete command.
mCommissioner->ExtendArmFailSafe(mCommissioneeDeviceProxy, CommissioningStage::kFindOperational,
mParams.GetCASEFailsafeTimerSeconds().Value(),
GetCommandTimeout(mCommissioneeDeviceProxy, CommissioningStage::kArmFailsafe));
GetCommandTimeout(mCommissioneeDeviceProxy, CommissioningStage::kArmFailsafe),
OnExtendFailsafeSuccessForCASE, OnFailsafeFailureForCASE);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/controller/AutoCommissioner.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ class AutoCommissioner : public CommissioningDelegate

private:
DeviceProxy * GetDeviceProxyForStep(CommissioningStage nextStage);
void finalizePASESession();

// Adjust the failsafe timer if CommissioningDelegate GetCASEFailsafeTimerSeconds is set
void setCASEFailsafeTimerIfNeeded();
void ReleaseDAC();
void ReleasePAI();

Expand Down
16 changes: 3 additions & 13 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1084,26 +1084,16 @@ void DeviceCommissioner::OnFailedToExtendedArmFailSafeDeviceAttestation(void * c
commissioner->CommissioningStageComplete(CHIP_ERROR_INTERNAL, report);
}

void OnExtendFailsafeFailure(void * context, CHIP_ERROR error)
{
ChipLogProgress(Controller, "ExtendFailsafe received failure response %s\n", chip::ErrorStr(error));
}

void OnExtendFailsafeSuccess(void * context, const GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data)
{
ChipLogProgress(Controller, "ExtendFailsafe received ArmFailSafe response errorCode=%u", to_underlying(data.errorCode));
}

void DeviceCommissioner::ExtendArmFailSafe(DeviceProxy * proxy, CommissioningStage step, uint16_t armFailSafeTimeout,
Optional<System::Clock::Timeout> timeout)
Optional<System::Clock::Timeout> timeout, OnExtendFailsafeSuccess onSuccess,
OnExtendFailsafeFailure onFailure)
{
uint64_t breadcrumb = static_cast<uint64_t>(step);
GeneralCommissioning::Commands::ArmFailSafe::Type request;
request.expiryLengthSeconds = armFailSafeTimeout;
request.breadcrumb = breadcrumb;
ChipLogProgress(Controller, "Arming failsafe for CASE (%u seconds)", request.expiryLengthSeconds);
SendCommand<GeneralCommissioningCluster>(proxy, request, OnExtendFailsafeSuccess, OnExtendFailsafeFailure, kRootEndpointId,
timeout);
SendCommand<GeneralCommissioningCluster>(proxy, request, onSuccess, onFailure, kRootEndpointId, timeout);
}

void DeviceCommissioner::ExtendArmFailSafeForDeviceAttestation(const Credentials::DeviceAttestationVerifier::AttestationInfo & info,
Expand Down
10 changes: 9 additions & 1 deletion src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,13 @@ using UdcTransportMgr = TransportMgr<Transport::UDP /* IPv6 */
>;
#endif

/**
* @brief Callback prototype for ExtendArmFailSafe command.
*/
typedef void (*OnExtendFailsafeSuccess)(
void * context, const app::Clusters::GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data);
typedef void (*OnExtendFailsafeFailure)(void * context, CHIP_ERROR error);

/**
* @brief
* The commissioner applications can use this class to pair new/unpaired CHIP devices. The application is
Expand Down Expand Up @@ -672,7 +679,8 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,

// Reset the arm failsafe timer during commissioning.
void ExtendArmFailSafe(DeviceProxy * proxy, CommissioningStage step, uint16_t armFailSafeTimeout,
Optional<System::Clock::Timeout> timeout);
Optional<System::Clock::Timeout> timeout, OnExtendFailsafeSuccess onSuccess,
OnExtendFailsafeFailure onFailure);

private:
DevicePairingDelegate * mPairingDelegate;
Expand Down
3 changes: 3 additions & 0 deletions src/controller/CommissioningDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ enum CommissioningStage : uint8_t
/// ScanNetworks can happen anytime after kArmFailsafe.
/// However, the cirque tests fail if it is earlier in the list
kScanNetworks,
/// Waiting for the higher layer to provide network credentials before continuing the workflow.
/// Call CHIPDeviceController::NetworkCredentialsReady() when CommissioningParameters is populated with
/// network credentials to use in kWiFiNetworkSetup or kThreadNetworkSetup steps.
kNeedsNetworkCreds,
};

Expand Down