Matter SDK Coverage Report
Current view: top level - app - OperationalSessionSetup.cpp (source / functions) Coverage Total Hit
Test: SHA:e021a368d10ac6f3f201c101585146211fdcdaa2 Lines: 0.0 % 300 0
Test Date: 2026-02-13 08:13:38 Functions: 0.0 % 27 0

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2020-2021 Project CHIP Authors
       4              :  *    All rights reserved.
       5              :  *
       6              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7              :  *    you may not use this file except in compliance with the License.
       8              :  *    You may obtain a copy of the License at
       9              :  *
      10              :  *        http://www.apache.org/licenses/LICENSE-2.0
      11              :  *
      12              :  *    Unless required by applicable law or agreed to in writing, software
      13              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15              :  *    See the License for the specific language governing permissions and
      16              :  *    limitations under the License.
      17              :  */
      18              : 
      19              : /**
      20              :  *  @file
      21              :  *    This file contains implementation of Device class. The objects of this
      22              :  *    class will be used by Controller applications to interact with CHIP
      23              :  *    devices. The class provides mechanism to construct, send and receive
      24              :  *    messages to and from the corresponding CHIP devices.
      25              :  */
      26              : 
      27              : #include <app/OperationalSessionSetup.h>
      28              : 
      29              : #include <app/CASEClient.h>
      30              : #include <app/InteractionModelEngine.h>
      31              : #include <transport/SecureSession.h>
      32              : 
      33              : #include <lib/address_resolve/AddressResolve.h>
      34              : #include <lib/core/CHIPCore.h>
      35              : #include <lib/core/CHIPEncoding.h>
      36              : #include <lib/dnssd/Resolver.h>
      37              : #include <lib/support/CodeUtils.h>
      38              : #include <lib/support/logging/CHIPLogging.h>
      39              : #include <system/SystemClock.h>
      40              : #include <system/SystemLayer.h>
      41              : #include <tracing/metric_event.h>
      42              : 
      43              : using namespace chip::Callback;
      44              : using chip::AddressResolve::NodeLookupRequest;
      45              : using chip::AddressResolve::Resolver;
      46              : using chip::AddressResolve::ResolveResult;
      47              : using namespace chip::Tracing;
      48              : 
      49              : namespace chip {
      50              : 
      51            0 : void OperationalSessionSetup::MoveToState(State aTargetState)
      52              : {
      53            0 :     if (mState != aTargetState)
      54              :     {
      55            0 :         ChipLogDetail(Discovery, "OperationalSessionSetup[%u:" ChipLogFormatX64 "]: State change %d --> %d",
      56              :                       mPeerId.GetFabricIndex(), ChipLogValueX64(mPeerId.GetNodeId()), to_underlying(mState),
      57              :                       to_underlying(aTargetState));
      58              : 
      59              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
      60            0 :         if (mState == State::WaitingForRetry)
      61              :         {
      62            0 :             CancelSessionSetupReattempt();
      63              :         }
      64              : #endif
      65              : 
      66            0 :         mState = aTargetState;
      67              : 
      68            0 :         if (aTargetState != State::Connecting)
      69              :         {
      70            0 :             CleanupCASEClient();
      71              :         }
      72              :     }
      73            0 : }
      74              : 
      75            0 : bool OperationalSessionSetup::AttachToExistingSecureSession()
      76              : {
      77            0 :     VerifyOrReturnError(mState == State::NeedsAddress || mState == State::ResolvingAddress || mState == State::HasAddress ||
      78              :                             mState == State::WaitingForRetry,
      79              :                         false);
      80              : 
      81            0 :     auto sessionHandle = mInitParams.sessionManager->FindSecureSessionForNode(
      82            0 :         mPeerId, MakeOptional(Transport::SecureSession::Type::kCASE), mTransportPayloadCapability);
      83            0 :     if (!sessionHandle.HasValue())
      84            0 :         return false;
      85              : 
      86            0 :     ChipLogProgress(Discovery, "Found an existing secure session to [%u:" ChipLogFormatX64 "]!", mPeerId.GetFabricIndex(),
      87              :                     ChipLogValueX64(mPeerId.GetNodeId()));
      88              : 
      89            0 :     mDeviceAddress = sessionHandle.Value()->AsSecureSession()->GetPeerAddress();
      90            0 :     if (!mSecureSession.Grab(sessionHandle.Value()))
      91            0 :         return false;
      92              : 
      93            0 :     return true;
      94            0 : }
      95              : 
      96            0 : void OperationalSessionSetup::Connect(Callback::Callback<OnDeviceConnected> * onConnection,
      97              :                                       Callback::Callback<OnDeviceConnectionFailure> * onFailure,
      98              :                                       Callback::Callback<OnSetupFailure> * onSetupFailure,
      99              :                                       TransportPayloadCapability transportPayloadCapability)
     100              : {
     101            0 :     CHIP_ERROR err   = CHIP_NO_ERROR;
     102            0 :     bool isConnected = false;
     103              : 
     104            0 :     mTransportPayloadCapability = transportPayloadCapability;
     105              :     //
     106              :     // Always enqueue our user provided callbacks into our callback list.
     107              :     // If anything goes wrong below, we'll trigger failures (including any queued from
     108              :     // a previous iteration which in theory shouldn't happen, but this is written to be more defensive)
     109              :     //
     110            0 :     EnqueueConnectionCallbacks(onConnection, onFailure, onSetupFailure);
     111              : 
     112            0 :     switch (mState)
     113              :     {
     114            0 :     case State::Uninitialized:
     115            0 :         err = CHIP_ERROR_INCORRECT_STATE;
     116            0 :         break;
     117              : 
     118            0 :     case State::NeedsAddress:
     119            0 :         isConnected = AttachToExistingSecureSession();
     120            0 :         if (!isConnected)
     121              :         {
     122              :             // LookupPeerAddress could perhaps call back with a result
     123              :             // synchronously, so do our state update first.
     124            0 :             MoveToState(State::ResolvingAddress);
     125            0 :             err = LookupPeerAddress();
     126            0 :             if (err != CHIP_NO_ERROR)
     127              :             {
     128              :                 // Roll back the state change, since we are presumably not in
     129              :                 // the middle of a lookup.
     130            0 :                 MoveToState(State::NeedsAddress);
     131              :             }
     132              :         }
     133              : 
     134            0 :         break;
     135              : 
     136            0 :     case State::ResolvingAddress:
     137              :     case State::WaitingForRetry:
     138            0 :         isConnected = AttachToExistingSecureSession();
     139            0 :         break;
     140              : 
     141            0 :     case State::HasAddress:
     142            0 :         isConnected = AttachToExistingSecureSession();
     143            0 :         if (!isConnected)
     144              :         {
     145              :             // We should not actually every be in be in State::HasAddress. This
     146              :             // is because in the same call that we moved to State::HasAddress
     147              :             // we either move to State::Connecting or call
     148              :             // DequeueConnectionCallbacks with an error thus releasing
     149              :             // ourselves before any call would reach this section of code.
     150            0 :             err = CHIP_ERROR_INCORRECT_STATE;
     151              :         }
     152              : 
     153            0 :         break;
     154              : 
     155            0 :     case State::Connecting:
     156            0 :         break;
     157              : 
     158            0 :     case State::SecureConnected:
     159            0 :         isConnected = true;
     160            0 :         break;
     161              : 
     162            0 :     default:
     163            0 :         err = CHIP_ERROR_INCORRECT_STATE;
     164              :     }
     165              : 
     166            0 :     if (isConnected)
     167              :     {
     168            0 :         MoveToState(State::SecureConnected);
     169              :     }
     170              : 
     171              :     //
     172              :     // Dequeue all our callbacks on either encountering an error
     173              :     // or if we successfully connected. Both should not be set
     174              :     // simultaneously.
     175              :     //
     176            0 :     if (err != CHIP_NO_ERROR || isConnected)
     177              :     {
     178            0 :         DequeueConnectionCallbacks(err);
     179              :         // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     180              :         // While it is odd to have an explicit return here at the end of the function, we do so
     181              :         // as a precaution in case someone later on adds something to the end of this function.
     182            0 :         return;
     183              :     }
     184              : }
     185              : 
     186            0 : void OperationalSessionSetup::Connect(Callback::Callback<OnDeviceConnected> * onConnection,
     187              :                                       Callback::Callback<OnDeviceConnectionFailure> * onFailure,
     188              :                                       TransportPayloadCapability transportPayloadCapability)
     189              : {
     190            0 :     Connect(onConnection, onFailure, nullptr, transportPayloadCapability);
     191            0 : }
     192              : 
     193            0 : void OperationalSessionSetup::Connect(Callback::Callback<OnDeviceConnected> * onConnection,
     194              :                                       Callback::Callback<OnSetupFailure> * onSetupFailure,
     195              :                                       TransportPayloadCapability transportPayloadCapability)
     196              : {
     197            0 :     Connect(onConnection, nullptr, onSetupFailure, transportPayloadCapability);
     198            0 : }
     199              : 
     200            0 : void OperationalSessionSetup::UpdateDeviceData(const ResolveResult & result)
     201              : {
     202            0 :     auto & config = result.mrpRemoteConfig;
     203            0 :     auto addr     = result.address;
     204              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     205              :     // Make sure to clear out our reason for trying the next result first thing,
     206              :     // so it does not stick around in various error cases.
     207            0 :     bool tryingNextResultDueToSessionEstablishmentError = mTryingNextResultDueToSessionEstablishmentError;
     208            0 :     mTryingNextResultDueToSessionEstablishmentError     = false;
     209              : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     210              : 
     211            0 :     if (mState == State::Uninitialized)
     212              :     {
     213            0 :         return;
     214              :     }
     215              : 
     216              : #if CHIP_DETAIL_LOGGING
     217              :     char peerAddrBuff[Transport::PeerAddress::kMaxToStringSize];
     218            0 :     addr.ToString(peerAddrBuff);
     219              : 
     220            0 :     ChipLogDetail(Discovery, "OperationalSessionSetup[%u:" ChipLogFormatX64 "]: Updating device address to %s while in state %d",
     221              :                   mPeerId.GetFabricIndex(), ChipLogValueX64(mPeerId.GetNodeId()), peerAddrBuff, static_cast<int>(mState));
     222              : #endif
     223              : 
     224            0 :     mDeviceAddress = addr;
     225              : 
     226              :     // Initialize CASE session state with any MRP parameters that DNS-SD has provided.
     227              :     // It can be overridden by CASE session protocol messages that include MRP parameters.
     228            0 :     if (mCASEClient)
     229              :     {
     230            0 :         mCASEClient->SetRemoteMRPIntervals(config);
     231              :     }
     232              : 
     233            0 :     if (mState != State::ResolvingAddress)
     234              :     {
     235            0 :         ChipLogError(Discovery, "Received UpdateDeviceData in incorrect state");
     236            0 :         DequeueConnectionCallbacks(CHIP_ERROR_INCORRECT_STATE);
     237              :         // Do not touch `this` instance anymore; it has been destroyed in
     238              :         // DequeueConnectionCallbacks.
     239            0 :         return;
     240              :     }
     241              : 
     242            0 :     MoveToState(State::HasAddress);
     243            0 :     mInitParams.sessionManager->UpdateAllSessionsPeerAddress(mPeerId, addr);
     244              : 
     245            0 :     if (mPerformingAddressUpdate)
     246              :     {
     247              :         // Nothing else to do here.
     248            0 :         DequeueConnectionCallbacks(CHIP_NO_ERROR);
     249              :         // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     250            0 :         return;
     251              :     }
     252              : 
     253            0 :     CHIP_ERROR err = EstablishConnection(result);
     254            0 :     LogErrorOnFailure(err);
     255            0 :     if (err == CHIP_NO_ERROR)
     256              :     {
     257              :         // We expect to get a callback via OnSessionEstablished or OnSessionEstablishmentError to continue
     258              :         // the state machine forward.
     259              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     260            0 :         if (tryingNextResultDueToSessionEstablishmentError)
     261              :         {
     262              :             // Our retry has already been kicked off, so claim 0 delay until it
     263              :             // starts.  We only reach this from OnSessionEstablishmentError when
     264              :             // the error is CHIP_ERROR_TIMEOUT.
     265            0 :             NotifyRetryHandlers(CHIP_ERROR_TIMEOUT, config, System::Clock::kZero);
     266              :         }
     267              : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     268            0 :         return;
     269              :     }
     270              : 
     271              :     // Move to the ResolvingAddress state, in case we have more results,
     272              :     // since we expect to receive results in that state.  Pretend like we moved
     273              :     // on directly to this address from whatever triggered us to try this result
     274              :     // (so restore mTryingNextResultDueToSessionEstablishmentError to the value
     275              :     // it had at the start of this function).
     276            0 :     MoveToState(State::ResolvingAddress);
     277              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     278            0 :     mTryingNextResultDueToSessionEstablishmentError = tryingNextResultDueToSessionEstablishmentError;
     279              : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     280            0 :     if (CHIP_NO_ERROR == Resolver::Instance().TryNextResult(mAddressLookupHandle))
     281              :     {
     282              :         // No need to NotifyRetryHandlers, since we never actually spent any
     283              :         // time trying the previous result.  Whatever work we need to do has
     284              :         // been handled by our recursive OnNodeAddressResolved callback.  Make
     285              :         // sure not to touch `this` under here, because it might have been
     286              :         // deleted by OnNodeAddressResolved.
     287            0 :         return;
     288              :     }
     289              : 
     290              :     // No need to reset mTryingNextResultDueToSessionEstablishmentError here,
     291              :     // because we're about to delete ourselves.
     292              : 
     293            0 :     DequeueConnectionCallbacks(err);
     294              :     // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     295              : }
     296              : 
     297            0 : CHIP_ERROR OperationalSessionSetup::EstablishConnection(const ResolveResult & result)
     298              : {
     299            0 :     auto config = result.mrpRemoteConfig;
     300              : 
     301            0 :     if (result.isICDOperatingAsLIT)
     302              :     {
     303              :         // When an ICD operates as a LIT, the DNS-SD advertisement lacks the Session Idle Interval
     304              :         // (SII). This would cause mIdleRetransTimeout to be 0, which is not a usable value. Since
     305              :         // CASE is established with LIT ICDs only when they are active, we can base
     306              :         // mIdleRetransTimeout on active mode parameters. To ensure sufficient time for MRP
     307              :         // retransmissions, particularly in Thread networks where mActiveRetransTimeout might be too
     308              :         // small, we use the maximum of config.mActiveRetransTimeout and
     309              :         // mInitParams.minimumLITBackoffInterval
     310              : 
     311            0 :         config.mIdleRetransTimeout =
     312            0 :             std::max(config.mActiveRetransTimeout, System::Clock::Milliseconds32(mInitParams.minimumLITBackoffInterval.ValueOr(0)));
     313              :     }
     314              : #if INET_CONFIG_ENABLE_TCP_ENDPOINT
     315            0 :     if (mTransportPayloadCapability == TransportPayloadCapability::kLargePayload)
     316              :     {
     317            0 :         if (result.supportsTcpServer)
     318              :         {
     319              :             // Set the transport type for carrying large payloads
     320            0 :             mDeviceAddress.SetTransportType(chip::Transport::Type::kTcp);
     321              :         }
     322              :         else
     323              :         {
     324              :             // we should not set the large payload while the TCP support is not enabled
     325            0 :             ChipLogError(
     326              :                 Discovery,
     327              :                 "LargePayload session requested but peer does not support TCP server, PeerNodeId=" ChipLogFormatScopedNodeId,
     328              :                 ChipLogValueScopedNodeId(mPeerId));
     329            0 :             return CHIP_ERROR_INTERNAL;
     330              :         }
     331              :     }
     332              : #endif
     333              : 
     334            0 :     mCASEClient = mClientPool->Allocate();
     335            0 :     VerifyOrReturnError(mCASEClient != nullptr, CHIP_ERROR_NO_MEMORY);
     336              : 
     337              :     MATTER_LOG_METRIC_BEGIN(kMetricDeviceCASESession);
     338            0 :     CHIP_ERROR err = mCASEClient->EstablishSession(mInitParams, mPeerId, mDeviceAddress, config, this);
     339            0 :     if (err != CHIP_NO_ERROR)
     340              :     {
     341              :         MATTER_LOG_METRIC_END(kMetricDeviceCASESession, err);
     342            0 :         CleanupCASEClient();
     343            0 :         return err;
     344              :     }
     345              : 
     346            0 :     MoveToState(State::Connecting);
     347              : 
     348            0 :     return CHIP_NO_ERROR;
     349              : }
     350              : 
     351            0 : void OperationalSessionSetup::EnqueueConnectionCallbacks(Callback::Callback<OnDeviceConnected> * onConnection,
     352              :                                                          Callback::Callback<OnDeviceConnectionFailure> * onFailure,
     353              :                                                          Callback::Callback<OnSetupFailure> * onSetupFailure)
     354              : {
     355            0 :     mCallbacks.Enqueue(onConnection, onFailure, onSetupFailure);
     356            0 : }
     357              : 
     358            0 : void OperationalSessionSetup::DequeueConnectionCallbacks(CHIP_ERROR error, SessionEstablishmentStage stage,
     359              :                                                          ReleaseBehavior releaseBehavior)
     360              : {
     361              :     // We expect that we only have callbacks if we are not performing just address update.
     362            0 :     VerifyOrDie(!mPerformingAddressUpdate || mCallbacks.IsEmpty());
     363              : 
     364              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     365              :     // Clear out mConnectionRetry, so that those cancelables are not holding
     366              :     // pointers to us, since we're about to go away.
     367            0 :     while (auto * cb = mConnectionRetry.First())
     368              :     {
     369            0 :         cb->Cancel();
     370            0 :     }
     371              : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     372              : 
     373              :     // Gather up state we will need for our notifications.
     374            0 :     SuccessFailureCallbackList readyCallbacks;
     375            0 :     readyCallbacks.EnqueueTakeAll(mCallbacks);
     376            0 :     auto * exchangeMgr                            = mInitParams.exchangeMgr;
     377            0 :     Optional<SessionHandle> optionalSessionHandle = mSecureSession.Get();
     378            0 :     ScopedNodeId peerId                           = mPeerId;
     379            0 :     System::Clock::Milliseconds16 requestedBusyDelay =
     380              : #if CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
     381              :         mRequestedBusyDelay;
     382              : #else
     383              :         System::Clock::kZero;
     384              : #endif // CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
     385              : 
     386            0 :     if (releaseBehavior == ReleaseBehavior::Release)
     387              :     {
     388            0 :         VerifyOrDie(mReleaseDelegate != nullptr);
     389            0 :         mReleaseDelegate->ReleaseSession(this);
     390              :     }
     391              : 
     392              :     // DO NOT touch any members of this object after this point.  It's dead.
     393            0 :     NotifyConnectionCallbacks(readyCallbacks, error, stage, peerId, exchangeMgr, optionalSessionHandle, requestedBusyDelay);
     394            0 : }
     395              : 
     396            0 : void OperationalSessionSetup::NotifyConnectionCallbacks(SuccessFailureCallbackList & ready, CHIP_ERROR error,
     397              :                                                         SessionEstablishmentStage stage, const ScopedNodeId & peerId,
     398              :                                                         Messaging::ExchangeManager * exchangeMgr,
     399              :                                                         const Optional<SessionHandle> & optionalSessionHandle,
     400              :                                                         System::Clock::Milliseconds16 requestedBusyDelay)
     401              : {
     402              :     Callback::Callback<OnDeviceConnected> * onConnected;
     403              :     Callback::Callback<OnDeviceConnectionFailure> * onConnectionFailure;
     404              :     Callback::Callback<OnSetupFailure> * onSetupFailure;
     405            0 :     while (ready.Take(onConnected, onConnectionFailure, onSetupFailure))
     406              :     {
     407            0 :         if (error == CHIP_NO_ERROR)
     408              :         {
     409            0 :             VerifyOrDie(exchangeMgr);
     410            0 :             VerifyOrDie(optionalSessionHandle.Value()->AsSecureSession()->IsActiveSession());
     411            0 :             if (onConnected != nullptr)
     412              :             {
     413            0 :                 onConnected->mCall(onConnected->mContext, *exchangeMgr, optionalSessionHandle.Value());
     414              : 
     415              :                 // That sucessful call might have made the session inactive.  If it did, then we should
     416              :                 // not call any more success callbacks, since we do not in fact have an active session
     417              :                 // for them, and if they try to put the session in a holder that will fail, and then
     418              :                 // trying to use the holder as if it has a session will crash.
     419            0 :                 if (!optionalSessionHandle.Value()->AsSecureSession()->IsActiveSession())
     420              :                 {
     421            0 :                     ChipLogError(Discovery, "Success callback for connection to " ChipLogFormatScopedNodeId " tore down session",
     422              :                                  ChipLogValueScopedNodeId(peerId));
     423            0 :                     error = CHIP_ERROR_CONNECTION_ABORTED;
     424              :                 }
     425              :             }
     426              :         }
     427              :         else // error
     428              :         {
     429            0 :             if (onConnectionFailure != nullptr)
     430              :             {
     431            0 :                 onConnectionFailure->mCall(onConnectionFailure->mContext, peerId, error);
     432              :             }
     433            0 :             if (onSetupFailure != nullptr)
     434              :             {
     435            0 :                 ConnectionFailureInfo failureInfo(peerId, error, stage);
     436              : #if CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
     437            0 :                 if (error == CHIP_ERROR_BUSY)
     438              :                 {
     439            0 :                     failureInfo.requestedBusyDelay.Emplace(requestedBusyDelay);
     440              :                 }
     441              : #endif // CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
     442            0 :                 onSetupFailure->mCall(onSetupFailure->mContext, failureInfo);
     443              :             }
     444              :         }
     445              :     }
     446            0 : }
     447              : 
     448            0 : void OperationalSessionSetup::OnSessionEstablishmentError(CHIP_ERROR error, SessionEstablishmentStage stage)
     449              : {
     450            0 :     VerifyOrReturn(mState == State::Connecting,
     451              :                    ChipLogError(Discovery, "OnSessionEstablishmentError was called while we were not connecting"));
     452              : 
     453              :     // If this condition ever changes, we may need to store the error in a
     454              :     // member instead of having a boolean
     455              :     // mTryingNextResultDueToSessionEstablishmentError, so we can recover the
     456              :     // error in UpdateDeviceData.
     457            0 :     if (CHIP_ERROR_TIMEOUT == error || CHIP_ERROR_BUSY == error)
     458              :     {
     459              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     460              :         // Make a copy of the ReliableMessageProtocolConfig, since our
     461              :         // mCaseClient is about to go away once we change state.
     462            0 :         ReliableMessageProtocolConfig remoteMprConfig = mCASEClient->GetRemoteMRPIntervals();
     463              : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     464              : 
     465              :         // Move to the ResolvingAddress state, in case we have more results,
     466              :         // since we expect to receive results in that state.
     467            0 :         MoveToState(State::ResolvingAddress);
     468              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     469            0 :         mTryingNextResultDueToSessionEstablishmentError = true;
     470              : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     471            0 :         if (CHIP_NO_ERROR == Resolver::Instance().TryNextResult(mAddressLookupHandle))
     472              :         {
     473              :             // Whatever work we needed to do has been handled by our
     474              :             // OnNodeAddressResolved callback.  Make sure not to touch `this`
     475              :             // under here, because it might have been deleted by
     476              :             // OnNodeAddressResolved.
     477            0 :             return;
     478              :         }
     479              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     480            0 :         mTryingNextResultDueToSessionEstablishmentError = false;
     481              : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     482              : 
     483              :         // Moving back to the Connecting state would be a bit of a lie, since we
     484              :         // don't have an mCASEClient.  Just go back to NeedsAddress, since
     485              :         // that's really where we are now.
     486            0 :         MoveToState(State::NeedsAddress);
     487              : 
     488              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     489            0 :         if (mRemainingAttempts > 0)
     490              :         {
     491              :             System::Clock::Seconds16 reattemptDelay;
     492            0 :             CHIP_ERROR err = ScheduleSessionSetupReattempt(reattemptDelay);
     493            0 :             if (err == CHIP_NO_ERROR)
     494              :             {
     495            0 :                 MoveToState(State::WaitingForRetry);
     496            0 :                 NotifyRetryHandlers(error, remoteMprConfig, reattemptDelay);
     497            0 :                 return;
     498              :             }
     499              :         }
     500              : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     501              :     }
     502              : 
     503              :     // Session failed to be established. This is when discovery is also stopped
     504              :     MATTER_LOG_METRIC_END(kMetricDeviceOperationalDiscovery, error);
     505              :     MATTER_LOG_METRIC_END(kMetricDeviceCASESession, error);
     506              : 
     507            0 :     DequeueConnectionCallbacks(error, stage);
     508              :     // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     509              : }
     510              : 
     511            0 : void OperationalSessionSetup::OnResponderBusy(System::Clock::Milliseconds16 requestedDelay)
     512              : {
     513              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES || CHIP_CONFIG_ENABLE_BUSY_HANDLING_FOR_OPERATIONAL_SESSION_SETUP
     514              :     // Store the requested delay, so that we can use it for scheduling our
     515              :     // retry or communicate it to our API consumer.
     516            0 :     mRequestedBusyDelay = requestedDelay;
     517              : #endif
     518            0 : }
     519              : 
     520            0 : void OperationalSessionSetup::OnSessionEstablished(const SessionHandle & session)
     521              : {
     522            0 :     VerifyOrReturn(mState == State::Connecting,
     523              :                    ChipLogError(Discovery, "OnSessionEstablished was called while we were not connecting"));
     524              : 
     525              :     // Session has been established. This is when discovery is also stopped
     526              :     MATTER_LOG_METRIC_END(kMetricDeviceOperationalDiscovery, CHIP_NO_ERROR);
     527              : 
     528              :     MATTER_LOG_METRIC_END(kMetricDeviceCASESession, CHIP_NO_ERROR);
     529              : 
     530            0 :     if (!mSecureSession.Grab(session))
     531              :     {
     532              :         // Got an invalid session, just dispatch an error.  We have to do this
     533              :         // so we don't leak.
     534            0 :         DequeueConnectionCallbacks(CHIP_ERROR_INCORRECT_STATE);
     535              : 
     536              :         // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     537            0 :         return;
     538              :     }
     539              : 
     540            0 :     MoveToState(State::SecureConnected);
     541              : 
     542            0 :     DequeueConnectionCallbacks(CHIP_NO_ERROR);
     543              : }
     544              : 
     545            0 : void OperationalSessionSetup::CleanupCASEClient()
     546              : {
     547            0 :     if (mCASEClient)
     548              :     {
     549            0 :         mClientPool->Release(mCASEClient);
     550            0 :         mCASEClient = nullptr;
     551              :     }
     552            0 : }
     553              : 
     554            0 : OperationalSessionSetup::~OperationalSessionSetup()
     555              : {
     556            0 :     if (mAddressLookupHandle.IsActive())
     557              :     {
     558            0 :         ChipLogDetail(Discovery,
     559              :                       "OperationalSessionSetup[%u:" ChipLogFormatX64
     560              :                       "]: Cancelling incomplete address resolution as device is being deleted.",
     561              :                       mPeerId.GetFabricIndex(), ChipLogValueX64(mPeerId.GetNodeId()));
     562              : 
     563              :         // Skip cancel callback since the destructor is being called, so we assume that this object is
     564              :         // obviously not used anymore
     565            0 :         CHIP_ERROR err = Resolver::Instance().CancelLookup(mAddressLookupHandle, Resolver::FailureCallback::Skip);
     566            0 :         if (err != CHIP_NO_ERROR)
     567              :         {
     568            0 :             ChipLogError(Discovery, "Lookup cancel failed: %" CHIP_ERROR_FORMAT, err.Format());
     569              :         }
     570              :     }
     571              : 
     572            0 :     if (mCASEClient)
     573              :     {
     574              :         // Make sure we don't leak it.
     575            0 :         mClientPool->Release(mCASEClient);
     576              :     }
     577              : 
     578              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     579            0 :     CancelSessionSetupReattempt();
     580              : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     581              : 
     582              : #if CHIP_CONFIG_ENABLE_ADDRESS_RESOLVE_FALLBACK
     583              :     CancelFallbackTimer();
     584              : #endif // CHIP_CONFIG_ENABLE_ADDRESS_RESOLVE_FALLBACK
     585              : 
     586            0 :     DequeueConnectionCallbacks(CHIP_ERROR_CANCELLED, ReleaseBehavior::DoNotRelease);
     587            0 : }
     588              : 
     589            0 : CHIP_ERROR OperationalSessionSetup::LookupPeerAddress()
     590              : {
     591              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     592            0 :     if (mRemainingAttempts > 0)
     593              :     {
     594            0 :         --mRemainingAttempts;
     595              :     }
     596            0 :     if (mAttemptsDone < UINT8_MAX)
     597              :     {
     598            0 :         ++mAttemptsDone;
     599              :     }
     600            0 :     if (mResolveAttemptsAllowed > 0)
     601              :     {
     602            0 :         --mResolveAttemptsAllowed;
     603              :     }
     604              :     MATTER_LOG_METRIC(kMetricDeviceOperationalDiscoveryAttemptCount, mAttemptsDone);
     605              : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     606              : 
     607              :     // NOTE: This is public API that can be used to update our stored peer
     608              :     // address even when we are in State::Connected, so we do not make any
     609              :     // MoveToState calls in this method.
     610            0 :     if (mAddressLookupHandle.IsActive())
     611              :     {
     612            0 :         ChipLogProgress(Discovery,
     613              :                         "OperationalSessionSetup[%u:" ChipLogFormatX64
     614              :                         "]: Operational node lookup already in progress. Will NOT start a new one.",
     615              :                         mPeerId.GetFabricIndex(), ChipLogValueX64(mPeerId.GetNodeId()));
     616            0 :         return CHIP_NO_ERROR;
     617              :     }
     618              : 
     619              :     // This code can be reached multiple times, if we discover multiple addresses or do retries.
     620              :     // The metric backend can handle this and always picks the earliest occurrence as the start of the event.
     621              :     MATTER_LOG_METRIC_BEGIN(kMetricDeviceOperationalDiscovery);
     622              : 
     623            0 :     auto const * fabricInfo = mInitParams.fabricTable->FindFabricWithIndex(mPeerId.GetFabricIndex());
     624            0 :     VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
     625              : 
     626            0 :     PeerId peerId(fabricInfo->GetCompressedFabricId(), mPeerId.GetNodeId());
     627              : 
     628            0 :     NodeLookupRequest request(peerId);
     629              : 
     630            0 :     CHIP_ERROR err = Resolver::Instance().LookupNode(request, mAddressLookupHandle);
     631              : 
     632              : #if CHIP_CONFIG_ENABLE_ADDRESS_RESOLVE_FALLBACK
     633              :     // Start fallback timer if we have a fallback result configured
     634              :     if (err == CHIP_NO_ERROR)
     635              :     {
     636              :         CHIP_ERROR fallbackErr = StartFallbackTimer();
     637              :         if (fallbackErr != CHIP_NO_ERROR)
     638              :         {
     639              :             ChipLogError(Discovery, "Failed to start fallback timer: %" CHIP_ERROR_FORMAT, fallbackErr.Format());
     640              :             // Continue anyway - fallback timer is optional
     641              :         }
     642              :     }
     643              : #endif // CHIP_CONFIG_ENABLE_ADDRESS_RESOLVE_FALLBACK
     644              : 
     645            0 :     return err;
     646              : }
     647              : 
     648            0 : void OperationalSessionSetup::PerformAddressUpdate()
     649              : {
     650            0 :     if (mPerformingAddressUpdate)
     651              :     {
     652              :         // We are already in the middle of a lookup from a previous call to
     653              :         // PerformAddressUpdate. In that case we will just exit right away as
     654              :         // we are already looking to update the results from the previous lookup.
     655            0 :         return;
     656              :     }
     657              : 
     658              :     // We must be newly-allocated to handle this address lookup, so must be in the NeedsAddress state.
     659            0 :     VerifyOrDie(mState == State::NeedsAddress);
     660              : 
     661              :     // We are doing an address lookup whether we have an active session for this peer or not.
     662            0 :     mPerformingAddressUpdate = true;
     663            0 :     MoveToState(State::ResolvingAddress);
     664            0 :     CHIP_ERROR err = LookupPeerAddress();
     665            0 :     if (err != CHIP_NO_ERROR)
     666              :     {
     667            0 :         ChipLogError(Discovery, "Failed to look up peer address: %" CHIP_ERROR_FORMAT, err.Format());
     668            0 :         DequeueConnectionCallbacks(err);
     669              :         // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     670            0 :         return;
     671              :     }
     672              : }
     673              : 
     674            0 : void OperationalSessionSetup::OnNodeAddressResolved(const PeerId & peerId, const ResolveResult & result)
     675              : {
     676              : #if CHIP_CONFIG_ENABLE_ADDRESS_RESOLVE_FALLBACK
     677              :     // DNS-SD resolution succeeded, cancel the fallback timer
     678              :     CancelFallbackTimer();
     679              : #endif // CHIP_CONFIG_ENABLE_ADDRESS_RESOLVE_FALLBACK
     680              : 
     681            0 :     UpdateDeviceData(result);
     682            0 : }
     683              : 
     684            0 : void OperationalSessionSetup::OnNodeAddressResolutionFailed(const PeerId & peerId, CHIP_ERROR reason)
     685              : {
     686            0 :     ChipLogError(Discovery, "OperationalSessionSetup[%u:" ChipLogFormatX64 "]: operational discovery failed: %" CHIP_ERROR_FORMAT,
     687              :                  mPeerId.GetFabricIndex(), ChipLogValueX64(mPeerId.GetNodeId()), reason.Format());
     688              : 
     689              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     690              :     // If we're in a mode where we would generally retry CASE, retry operational
     691              :     // discovery if we're allowed to.  That allows us to more-gracefully handle broken networks
     692              :     // where multicast DNS does not actually work and hence only the initial
     693              :     // unicast DNS-SD queries get a response.
     694              :     //
     695              :     // We check for State::ResolvingAddress just in case in the meantime
     696              :     // something weird happened and we are no longer trying to resolve an
     697              :     // address.
     698            0 :     if (mState == State::ResolvingAddress && mResolveAttemptsAllowed > 0)
     699              :     {
     700            0 :         ChipLogProgress(Discovery, "Retrying operational DNS-SD discovery. Attempts remaining: %u", mResolveAttemptsAllowed);
     701              : 
     702              :         // Pretend like our previous attempt (i.e. call to LookupPeerAddress)
     703              :         // has not happened for purposes of the generic attempt counters, so we
     704              :         // don't mess up the counters for our actual CASE retry logic.
     705            0 :         if (mRemainingAttempts < UINT8_MAX)
     706              :         {
     707            0 :             ++mRemainingAttempts;
     708              :         }
     709            0 :         if (mAttemptsDone > 0)
     710              :         {
     711            0 :             --mAttemptsDone;
     712              :         }
     713              : 
     714              :         MATTER_LOG_METRIC(kMetricDeviceOperationalDiscoveryAttemptCount, mAttemptsDone);
     715              : 
     716            0 :         CHIP_ERROR err = LookupPeerAddress();
     717            0 :         if (err == CHIP_NO_ERROR)
     718              :         {
     719              :             // We need to notify our consumer that the resolve will take more
     720              :             // time, but we don't actually know how much time it will take,
     721              :             // because the resolver does not expose that information.  Just use
     722              :             // one minute to be safe.
     723              :             using namespace chip::System::Clock::Literals;
     724            0 :             NotifyRetryHandlers(reason, 60_s16);
     725            0 :             return;
     726              :         }
     727              :     }
     728              : #endif
     729              : 
     730              :     MATTER_LOG_METRIC_END(kMetricDeviceOperationalDiscovery, reason);
     731              : 
     732              :     // No need to modify any variables in `this` since call below releases `this`.
     733            0 :     DequeueConnectionCallbacks(reason);
     734              :     // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     735              : }
     736              : 
     737              : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     738            0 : void OperationalSessionSetup::UpdateAttemptCount(uint8_t attemptCount)
     739              : {
     740            0 :     if (attemptCount == 0)
     741              :     {
     742              :         // Nothing to do.
     743            0 :         return;
     744              :     }
     745              : 
     746            0 :     if (mState != State::NeedsAddress)
     747              :     {
     748              :         // We're in the middle of an attempt already, so decrement attemptCount
     749              :         // by 1 to account for that.
     750            0 :         --attemptCount;
     751              :     }
     752              : 
     753            0 :     if (attemptCount > mRemainingAttempts)
     754              :     {
     755            0 :         mRemainingAttempts = attemptCount;
     756              :     }
     757              : 
     758            0 :     if (attemptCount > mResolveAttemptsAllowed)
     759              :     {
     760            0 :         mResolveAttemptsAllowed = attemptCount;
     761              :     }
     762              : }
     763              : 
     764            0 : CHIP_ERROR OperationalSessionSetup::ScheduleSessionSetupReattempt(System::Clock::Seconds16 & timerDelay)
     765              : {
     766            0 :     VerifyOrDie(mRemainingAttempts > 0);
     767              :     // Try again, but not if things are in shutdown such that we can't get
     768              :     // to a system layer, and not if we've run out of attempts.
     769            0 :     if (!mInitParams.exchangeMgr->GetSessionManager() || !mInitParams.exchangeMgr->GetSessionManager()->SystemLayer())
     770              :     {
     771            0 :         return CHIP_ERROR_INCORRECT_STATE;
     772              :     }
     773              : 
     774            0 :     MoveToState(State::NeedsAddress);
     775              :     // Stop exponential backoff before our delays get too large.
     776              :     //
     777              :     // Note that mAttemptsDone is always > 0 here, because we have
     778              :     // just finished one attempt.
     779            0 :     VerifyOrDie(mAttemptsDone > 0);
     780              :     static_assert(UINT16_MAX / CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_INITIAL_DELAY_SECONDS >=
     781              :                       (1 << CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_MAX_BACKOFF),
     782              :                   "Our backoff calculation will overflow.");
     783            0 :     System::Clock::Timeout actualTimerDelay = System::Clock::Seconds16(
     784            0 :         static_cast<uint16_t>(CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_INITIAL_DELAY_SECONDS
     785            0 :                               << std::min((mAttemptsDone - 1), CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_MAX_BACKOFF)));
     786            0 :     const bool responseWasBusy = mRequestedBusyDelay != System::Clock::kZero;
     787            0 :     if (responseWasBusy)
     788              :     {
     789            0 :         if (mRequestedBusyDelay > actualTimerDelay)
     790              :         {
     791            0 :             actualTimerDelay = mRequestedBusyDelay;
     792              :         }
     793              : 
     794              :         // Reset mRequestedBusyDelay now that we have consumed it, so it does
     795              :         // not affect future reattempts not triggered by a busy response.
     796            0 :         mRequestedBusyDelay = System::Clock::kZero;
     797              :     }
     798              : 
     799            0 :     if (mAttemptsDone % 2 == 0)
     800              :     {
     801              :         // It's possible that the other side received one of our Sigma1 messages
     802              :         // and then failed to get its Sigma2 back to us.  If that's the case, it
     803              :         // will be waiting for that Sigma2 to time out before it starts
     804              :         // listening for Sigma1 messages again.
     805              :         //
     806              :         // To handle that, on every other retry, add the amount of time it would
     807              :         // take the other side to time out.  It would be nice if we could rely
     808              :         // on the delay reported in a BUSY response to just tell us that value,
     809              :         // but in practice for old devices BUSY often sends some hardcoded value
     810              :         // that tells us nothing about when the other side will decide it has
     811              :         // timed out.
     812              :         //
     813              :         // Unfortunately, we do not have the MRP config for the other side here,
     814              :         // but in practice if the other side is using its local config to
     815              :         // compute Sigma2 response timeouts, then it's also returning useful
     816              :         // values with BUSY, so we will wait long enough.
     817            0 :         auto additionalTimeout = CASESession::ComputeSigma2ResponseTimeout(GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig()));
     818            0 :         actualTimerDelay += additionalTimeout;
     819              :     }
     820            0 :     timerDelay = std::chrono::duration_cast<System::Clock::Seconds16>(actualTimerDelay);
     821              : 
     822            0 :     CHIP_ERROR err = mInitParams.exchangeMgr->GetSessionManager()->SystemLayer()->StartTimer(actualTimerDelay, TrySetupAgain, this);
     823              : 
     824              :     // TODO: If responseWasBusy, should we increment, mRemainingAttempts and
     825              :     // mResolveAttemptsAllowed, since we were explicitly told to retry?  Hard to
     826              :     // tell what consumers expect out of a capped retry count here.
     827              : 
     828              :     // The cast on count() is needed because the type count() returns might not
     829              :     // actually be uint16_t; on some platforms it's int.
     830            0 :     ChipLogProgress(Discovery,
     831              :                     "OperationalSessionSetup:attempts done: %u, attempts left: %u, retry delay %us, status %" CHIP_ERROR_FORMAT,
     832              :                     mAttemptsDone, mRemainingAttempts, static_cast<unsigned>(timerDelay.count()), err.Format());
     833            0 :     return err;
     834              : }
     835              : 
     836            0 : void OperationalSessionSetup::CancelSessionSetupReattempt()
     837              : {
     838              :     // If we can't get a system layer, there is no way for us to cancel things
     839              :     // at this point, but hopefully that's because everything is torn down
     840              :     // anyway and hence the timer will not fire.
     841            0 :     auto * sessionManager = mInitParams.exchangeMgr->GetSessionManager();
     842            0 :     VerifyOrReturn(sessionManager != nullptr);
     843              : 
     844            0 :     auto * systemLayer = sessionManager->SystemLayer();
     845            0 :     VerifyOrReturn(systemLayer != nullptr);
     846              : 
     847            0 :     systemLayer->CancelTimer(TrySetupAgain, this);
     848              : }
     849              : 
     850            0 : void OperationalSessionSetup::TrySetupAgain(System::Layer * systemLayer, void * state)
     851              : {
     852            0 :     auto * self = static_cast<OperationalSessionSetup *>(state);
     853              : 
     854            0 :     self->MoveToState(State::ResolvingAddress);
     855            0 :     CHIP_ERROR err = self->LookupPeerAddress();
     856            0 :     if (err == CHIP_NO_ERROR)
     857              :     {
     858            0 :         return;
     859              :     }
     860              : 
     861              :     // Give up; we could not start a lookup.
     862            0 :     self->DequeueConnectionCallbacks(err);
     863              :     // Do not touch `self` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     864              : }
     865              : 
     866            0 : void OperationalSessionSetup::AddRetryHandler(Callback::Callback<OnDeviceConnectionRetry> * onRetry)
     867              : {
     868            0 :     mConnectionRetry.Enqueue(onRetry->Cancel());
     869            0 : }
     870              : 
     871            0 : void OperationalSessionSetup::NotifyRetryHandlers(CHIP_ERROR error, const ReliableMessageProtocolConfig & remoteMrpConfig,
     872              :                                                   System::Clock::Seconds16 retryDelay)
     873              : {
     874              :     // Compute the time we are likely to need to detect that the retry has
     875              :     // failed.
     876            0 :     System::Clock::Timeout messageTimeout = CASESession::ComputeSigma1ResponseTimeout(remoteMrpConfig);
     877            0 :     auto timeoutSecs                      = std::chrono::duration_cast<System::Clock::Seconds16>(messageTimeout);
     878              :     // Add 1 second in case we had fractional milliseconds in messageTimeout.
     879              :     using namespace chip::System::Clock::Literals;
     880            0 :     NotifyRetryHandlers(error, timeoutSecs + 1_s16 + retryDelay);
     881            0 : }
     882              : 
     883            0 : void OperationalSessionSetup::NotifyRetryHandlers(CHIP_ERROR error, System::Clock::Seconds16 timeoutEstimate)
     884              : {
     885              :     // We have to be very careful here: Calling into these handlers might in
     886              :     // theory destroy the Callback objects involved, but unlike the
     887              :     // succcess/failure cases we don't want to just clear the handlers from our
     888              :     // list when we are calling them, because we might need to call a given
     889              :     // handler more than once.
     890              :     //
     891              :     // To handle this we:
     892              :     //
     893              :     // 1) Snapshot the list of handlers up front, so if any of the handlers
     894              :     //    triggers an AddRetryHandler with some other handler that does not
     895              :     //    affect the list we plan to notify here.
     896              :     //
     897              :     // 2) When planning to notify a handler move it to a new list that contains
     898              :     //    just that handler.  This way if it gets canceled as part of the
     899              :     //    notification we can tell it has been canceled.
     900              :     //
     901              :     // 3) If notifying the handler does not cancel it, add it back to our list
     902              :     //    of handlers so we will notify it on future retries.
     903              : 
     904            0 :     Cancelable retryHandlerListSnapshot;
     905            0 :     mConnectionRetry.DequeueAll(retryHandlerListSnapshot);
     906              : 
     907            0 :     while (retryHandlerListSnapshot.mNext != &retryHandlerListSnapshot)
     908              :     {
     909            0 :         auto * cb = Callback::Callback<OnDeviceConnectionRetry>::FromCancelable(retryHandlerListSnapshot.mNext);
     910              : 
     911            0 :         Callback::CallbackDeque currentCallbackHolder;
     912            0 :         currentCallbackHolder.Enqueue(cb->Cancel());
     913              : 
     914            0 :         cb->mCall(cb->mContext, mPeerId, error, timeoutEstimate);
     915              : 
     916            0 :         if (currentCallbackHolder.mNext != &currentCallbackHolder)
     917              :         {
     918              :             // Callback has not been canceled as part of the call, so is still
     919              :             // supposed to be registered with us.
     920            0 :             AddRetryHandler(cb);
     921              :         }
     922            0 :     }
     923            0 : }
     924              : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     925              : 
     926              : #if CHIP_CONFIG_ENABLE_ADDRESS_RESOLVE_FALLBACK
     927              : void OperationalSessionSetup::SetFallbackResolveResult(const AddressResolve::ResolveResult & result)
     928              : {
     929              :     ChipLogProgress(Discovery, "OperationalSessionSetup[" ChipLogFormatScopedNodeId "]: Setting fallback resolve result",
     930              :                     ChipLogValueScopedNodeId(mPeerId));
     931              :     mFallbackResolveResult.SetValue(result);
     932              : }
     933              : 
     934              : System::Layer * OperationalSessionSetup::GetSystemLayer()
     935              : {
     936              :     auto * sessionManager = mInitParams.exchangeMgr->GetSessionManager();
     937              :     if (sessionManager == nullptr)
     938              :     {
     939              :         return nullptr;
     940              :     }
     941              :     return sessionManager->SystemLayer();
     942              : }
     943              : 
     944              : CHIP_ERROR OperationalSessionSetup::StartFallbackTimer()
     945              : {
     946              :     if (!mFallbackResolveResult.HasValue())
     947              :     {
     948              :         // No fallback configured, nothing to do
     949              :         return CHIP_NO_ERROR;
     950              :     }
     951              : 
     952              :     auto * systemLayer = GetSystemLayer();
     953              :     VerifyOrReturnError(systemLayer != nullptr, CHIP_ERROR_INCORRECT_STATE);
     954              : 
     955              :     ChipLogProgress(Discovery, "OperationalSessionSetup[" ChipLogFormatScopedNodeId "]: Starting fallback timer (%u seconds)",
     956              :                     ChipLogValueScopedNodeId(mPeerId),
     957              :                     static_cast<unsigned>(std::chrono::duration_cast<System::Clock::Seconds16>(mFallbackTimeout).count()));
     958              : 
     959              :     return systemLayer->StartTimer(mFallbackTimeout, OnFallbackTimeout, this);
     960              : }
     961              : 
     962              : void OperationalSessionSetup::CancelFallbackTimer()
     963              : {
     964              :     if (!mFallbackResolveResult.HasValue())
     965              :     {
     966              :         // No fallback configured, nothing to cancel
     967              :         return;
     968              :     }
     969              : 
     970              :     auto * systemLayer = GetSystemLayer();
     971              :     VerifyOrReturn(systemLayer != nullptr);
     972              : 
     973              :     ChipLogDetail(Discovery, "OperationalSessionSetup[" ChipLogFormatScopedNodeId "]: Cancelling fallback timer",
     974              :                   ChipLogValueScopedNodeId(mPeerId));
     975              : 
     976              :     systemLayer->CancelTimer(OnFallbackTimeout, this);
     977              : }
     978              : 
     979              : void OperationalSessionSetup::OnFallbackTimeout(System::Layer * systemLayer, void * appState)
     980              : {
     981              :     auto * self = static_cast<OperationalSessionSetup *>(appState);
     982              : 
     983              :     ChipLogProgress(Discovery,
     984              :                     "OperationalSessionSetup[" ChipLogFormatScopedNodeId "]: Address resolution timed out, using fallback address",
     985              :                     ChipLogValueScopedNodeId(self->mPeerId));
     986              : 
     987              :     // Cancel the ongoing address lookup
     988              :     if (self->mAddressLookupHandle.IsActive())
     989              :     {
     990              :         // Cancel the ongoing DNS-SD lookup using FailureCallback::Skip to prevent double error handling.
     991              :         // If cancellation fails (logged below), we proceed with fallback anyway since the timer has expired.
     992              :         // This is safe because we have a known-good address from the PASE session.
     993              :         CHIP_ERROR err = Resolver::Instance().CancelLookup(self->mAddressLookupHandle, Resolver::FailureCallback::Skip);
     994              :         if (err != CHIP_NO_ERROR)
     995              :         {
     996              :             ChipLogError(Discovery, "Failed to cancel address lookup: %" CHIP_ERROR_FORMAT, err.Format());
     997              :         }
     998              :     }
     999              : 
    1000              :     // Use the fallback result. This must have a value because StartFallbackTimer only starts
    1001              :     // the timer when mFallbackResolveResult has a value. If this fails, it indicates memory
    1002              :     // corruption or a serious programming error.
    1003              :     VerifyOrDie(self->mFallbackResolveResult.HasValue());
    1004              :     self->UpdateDeviceData(self->mFallbackResolveResult.Value());
    1005              :     // Do not touch `self` instance anymore; it might have been destroyed in UpdateDeviceData.
    1006              : }
    1007              : #endif // CHIP_CONFIG_ENABLE_ADDRESS_RESOLVE_FALLBACK
    1008              : 
    1009              : } // namespace chip
        

Generated by: LCOV version 2.0-1