diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 20b47d3ff8e985..8f81d4f5fec327 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -77,6 +77,7 @@ buildconfig_header("app_buildconfig") { "TIME_SYNC_ENABLE_TSC_FEATURE=${time_sync_enable_tsc_feature}", "NON_SPEC_COMPLIANT_OTA_ACTION_DELAY_FLOOR=${non_spec_compliant_ota_action_delay_floor}", "CHIP_DEVICE_CONFIG_DYNAMIC_SERVER=${chip_build_controller_dynamic_server}", + "CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP=${chip_enable_busy_handling_for_operational_session_setup}", ] visibility = [ ":app_config" ] diff --git a/src/app/OperationalSessionSetup.cpp b/src/app/OperationalSessionSetup.cpp index 6df07ca6b0568e..157de953116ef5 100644 --- a/src/app/OperationalSessionSetup.cpp +++ b/src/app/OperationalSessionSetup.cpp @@ -351,6 +351,12 @@ void OperationalSessionSetup::DequeueConnectionCallbacks(CHIP_ERROR error, Sessi auto * exchangeMgr = mInitParams.exchangeMgr; Optional optionalSessionHandle = mSecureSession.Get(); ScopedNodeId peerId = mPeerId; + System::Clock::Milliseconds16 requestedBusyDelay = +#if CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP + mRequestedBusyDelay; +#else + System::Clock::kZero; +#endif // CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP if (releaseBehavior == ReleaseBehavior::Release) { @@ -361,14 +367,15 @@ void OperationalSessionSetup::DequeueConnectionCallbacks(CHIP_ERROR error, Sessi // DO NOT touch any members of this object after this point. It's dead. NotifyConnectionCallbacks(failureReady, setupFailureReady, successReady, error, stage, peerId, performingAddressUpdate, - exchangeMgr, optionalSessionHandle); + exchangeMgr, optionalSessionHandle, requestedBusyDelay); } void OperationalSessionSetup::NotifyConnectionCallbacks(Cancelable & failureReady, Cancelable & setupFailureReady, Cancelable & successReady, CHIP_ERROR error, SessionEstablishmentStage stage, const ScopedNodeId & peerId, bool performingAddressUpdate, Messaging::ExchangeManager * exchangeMgr, - const Optional & optionalSessionHandle) + const Optional & optionalSessionHandle, + System::Clock::Milliseconds16 requestedBusyDelay) { // // If we encountered no error, go ahead and call all success callbacks. Otherwise, @@ -401,6 +408,12 @@ void OperationalSessionSetup::NotifyConnectionCallbacks(Cancelable & failureRead { // Initialize the ConnnectionFailureInfo object ConnnectionFailureInfo failureInfo(peerId, error, stage); +#if CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP + if (error == CHIP_ERROR_BUSY) + { + failureInfo.requestedBusyDelay.Emplace(requestedBusyDelay); + } +#endif // CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP cb->mCall(cb->mContext, failureInfo); } } @@ -482,9 +495,9 @@ void OperationalSessionSetup::OnSessionEstablishmentError(CHIP_ERROR error, Sess void OperationalSessionSetup::OnResponderBusy(System::Clock::Milliseconds16 requestedDelay) { -#if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES +#if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES || CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP // Store the requested delay, so that we can use it for scheduling our - // retry. + // retry or communicate it to our API consumer. mRequestedBusyDelay = requestedDelay; #endif } diff --git a/src/app/OperationalSessionSetup.h b/src/app/OperationalSessionSetup.h index 9d24faad5efabb..1de8c305353314 100644 --- a/src/app/OperationalSessionSetup.h +++ b/src/app/OperationalSessionSetup.h @@ -26,6 +26,7 @@ #pragma once +#include #include #include #include @@ -161,6 +162,13 @@ class DLL_EXPORT OperationalSessionSetup : public SessionEstablishmentDelegate, CHIP_ERROR error; SessionEstablishmentStage sessionStage; + // When the response was BUSY, error will be CHIP_ERROR_BUSY and + // requestedBusyDelay will be set, if handling of BUSY responses is + // enabled. +#if CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP + Optional requestedBusyDelay; +#endif // CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP + ConnnectionFailureInfo(const ScopedNodeId & peer, CHIP_ERROR err, SessionEstablishmentStage stage) : peerId(peer), error(err), sessionStage(stage) {} @@ -306,6 +314,10 @@ class DLL_EXPORT OperationalSessionSetup : public SessionEstablishmentDelegate, bool mPerformingAddressUpdate = false; +#if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES || CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP + System::Clock::Milliseconds16 mRequestedBusyDelay = System::Clock::kZero; +#endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES || CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP + #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES // When we TryNextResult on the resolver, it will synchronously call back // into our OnNodeAddressResolved when it succeeds. We need to track @@ -320,8 +332,6 @@ class DLL_EXPORT OperationalSessionSetup : public SessionEstablishmentDelegate, uint8_t mResolveAttemptsAllowed = 0; - System::Clock::Milliseconds16 mRequestedBusyDelay = System::Clock::kZero; - Callback::CallbackDeque mConnectionRetry; #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES @@ -385,7 +395,12 @@ class DLL_EXPORT OperationalSessionSetup : public SessionEstablishmentDelegate, Callback::Cancelable & successReady, CHIP_ERROR error, SessionEstablishmentStage stage, const ScopedNodeId & peerId, bool performingAddressUpdate, Messaging::ExchangeManager * exchangeMgr, - const Optional & optionalSessionHandle); + const Optional & optionalSessionHandle, + // requestedBusyDelay will be 0 if not + // CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP, + // and only has a meaningful value + // when the error is CHIP_ERROR_BUSY. + System::Clock::Milliseconds16 requestedBusyDelay); /** * Triggers a DNSSD lookup to find a usable peer address. diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp index 57ef57362a25fa..e8ada20a428c70 100644 --- a/src/app/ReadClient.cpp +++ b/src/app/ReadClient.cpp @@ -135,6 +135,11 @@ uint32_t ReadClient::ComputeTimeTillNextSubscription() waitTimeInMsec = minWaitTimeInMsec + (Crypto::GetRandU32() % (maxWaitTimeInMsec - minWaitTimeInMsec)); } + if (mMinimalResubscribeDelay.count() > waitTimeInMsec) + { + waitTimeInMsec = mMinimalResubscribeDelay.count(); + } + return waitTimeInMsec; } @@ -1058,6 +1063,10 @@ CHIP_ERROR ReadClient::ProcessSubscribeResponse(System::PacketBufferHandle && aP CHIP_ERROR ReadClient::SendAutoResubscribeRequest(ReadPrepareParams && aReadPrepareParams) { + // Make sure we don't use minimal resubscribe delays from previous attempts + // for this one. + mMinimalResubscribeDelay = System::Clock::kZero; + mReadPrepareParams = std::move(aReadPrepareParams); CHIP_ERROR err = SendSubscribeRequest(mReadPrepareParams); if (err != CHIP_NO_ERROR) @@ -1239,14 +1248,28 @@ void ReadClient::HandleDeviceConnected(void * context, Messaging::ExchangeManage } } -void ReadClient::HandleDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR err) +void ReadClient::HandleDeviceConnectionFailure(void * context, const OperationalSessionSetup::ConnnectionFailureInfo & failureInfo) { ReadClient * const _this = static_cast(context); VerifyOrDie(_this != nullptr); - ChipLogError(DataManagement, "Failed to establish CASE for re-subscription with error '%" CHIP_ERROR_FORMAT "'", err.Format()); + ChipLogError(DataManagement, "Failed to establish CASE for re-subscription with error '%" CHIP_ERROR_FORMAT "'", + failureInfo.error.Format()); + +#if CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP +#if CHIP_DETAIL_LOGGING + if (failureInfo.requestedBusyDelay.HasValue()) + { + ChipLogDetail(DataManagement, "Will delay resubscription by %u ms due to BUSY response", + failureInfo.requestedBusyDelay.Value().count()); + } +#endif // CHIP_DETAIL_LOGGING + _this->mMinimalResubscribeDelay = failureInfo.requestedBusyDelay.ValueOr(System::Clock::kZero); +#else + _this->mMinimalResubscribeDelay = System::Clock::kZero; +#endif // CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP - _this->Close(err); + _this->Close(failureInfo.error); } void ReadClient::OnResubscribeTimerCallback(System::Layer * /* If this starts being used, fix callers that pass nullptr */, diff --git a/src/app/ReadClient.h b/src/app/ReadClient.h index d3fb370febe25c..219a5be73f0b09 100644 --- a/src/app/ReadClient.h +++ b/src/app/ReadClient.h @@ -612,7 +612,7 @@ class ReadClient : public Messaging::ExchangeDelegate static void HandleDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle); - static void HandleDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR error); + static void HandleDeviceConnectionFailure(void * context, const OperationalSessionSetup::ConnnectionFailureInfo & failureInfo); CHIP_ERROR GetMinEventNumber(const ReadPrepareParams & aReadPrepareParams, Optional & aEventMin); @@ -642,8 +642,12 @@ class ReadClient : public Messaging::ExchangeDelegate bool mForceCaseOnNextResub = true; bool mIsResubscriptionScheduled = false; + // mMinimalResubscribeDelay is used to store the delay returned with a BUSY + // response to a Sigma1 message. + System::Clock::Milliseconds16 mMinimalResubscribeDelay = System::Clock::kZero; + chip::Callback::Callback mOnConnectedCallback; - chip::Callback::Callback mOnConnectionFailureCallback; + chip::Callback::Callback mOnConnectionFailureCallback; ReadClient * mpNext = nullptr; InteractionModelEngine * mpImEngine = nullptr; diff --git a/src/app/common_flags.gni b/src/app/common_flags.gni index 7565bfd223ff45..91057654fdcccf 100644 --- a/src/app/common_flags.gni +++ b/src/app/common_flags.gni @@ -17,4 +17,8 @@ declare_args() { chip_app_use_echo = false chip_enable_read_client = true chip_build_controller_dynamic_server = false + + # 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 }