LCOV - code coverage report
Current view: top level - app - OperationalSessionSetup.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 0 287 0.0 %
Date: 2024-02-15 08:20:41 Functions: 0 26 0.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             : 
      42             : using namespace chip::Callback;
      43             : using chip::AddressResolve::NodeLookupRequest;
      44             : using chip::AddressResolve::Resolver;
      45             : using chip::AddressResolve::ResolveResult;
      46             : 
      47             : namespace chip {
      48             : 
      49           0 : void OperationalSessionSetup::MoveToState(State aTargetState)
      50             : {
      51           0 :     if (mState != aTargetState)
      52             :     {
      53           0 :         ChipLogDetail(Discovery, "OperationalSessionSetup[%u:" ChipLogFormatX64 "]: State change %d --> %d",
      54             :                       mPeerId.GetFabricIndex(), ChipLogValueX64(mPeerId.GetNodeId()), to_underlying(mState),
      55             :                       to_underlying(aTargetState));
      56             : 
      57             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
      58           0 :         if (mState == State::WaitingForRetry)
      59             :         {
      60           0 :             CancelSessionSetupReattempt();
      61             :         }
      62             : #endif
      63             : 
      64           0 :         mState = aTargetState;
      65             : 
      66           0 :         if (aTargetState != State::Connecting)
      67             :         {
      68           0 :             CleanupCASEClient();
      69             :         }
      70             :     }
      71           0 : }
      72             : 
      73           0 : bool OperationalSessionSetup::AttachToExistingSecureSession()
      74             : {
      75           0 :     VerifyOrReturnError(mState == State::NeedsAddress || mState == State::ResolvingAddress || mState == State::HasAddress ||
      76             :                             mState == State::WaitingForRetry,
      77             :                         false);
      78             : 
      79             :     auto sessionHandle =
      80           0 :         mInitParams.sessionManager->FindSecureSessionForNode(mPeerId, MakeOptional(Transport::SecureSession::Type::kCASE));
      81           0 :     if (!sessionHandle.HasValue())
      82           0 :         return false;
      83             : 
      84           0 :     ChipLogProgress(Discovery, "Found an existing secure session to [%u:" ChipLogFormatX64 "]!", mPeerId.GetFabricIndex(),
      85             :                     ChipLogValueX64(mPeerId.GetNodeId()));
      86             : 
      87           0 :     mDeviceAddress = sessionHandle.Value()->AsSecureSession()->GetPeerAddress();
      88           0 :     if (!mSecureSession.Grab(sessionHandle.Value()))
      89           0 :         return false;
      90             : 
      91           0 :     return true;
      92           0 : }
      93             : 
      94           0 : void OperationalSessionSetup::Connect(Callback::Callback<OnDeviceConnected> * onConnection,
      95             :                                       Callback::Callback<OnDeviceConnectionFailure> * onFailure,
      96             :                                       Callback::Callback<OnSetupFailure> * onSetupFailure)
      97             : {
      98           0 :     CHIP_ERROR err   = CHIP_NO_ERROR;
      99           0 :     bool isConnected = false;
     100             : 
     101             :     //
     102             :     // Always enqueue our user provided callbacks into our callback list.
     103             :     // If anything goes wrong below, we'll trigger failures (including any queued from
     104             :     // a previous iteration which in theory shouldn't happen, but this is written to be more defensive)
     105             :     //
     106           0 :     EnqueueConnectionCallbacks(onConnection, onFailure, onSetupFailure);
     107             : 
     108           0 :     switch (mState)
     109             :     {
     110           0 :     case State::Uninitialized:
     111           0 :         err = CHIP_ERROR_INCORRECT_STATE;
     112           0 :         break;
     113             : 
     114           0 :     case State::NeedsAddress:
     115           0 :         isConnected = AttachToExistingSecureSession();
     116           0 :         if (!isConnected)
     117             :         {
     118             :             // LookupPeerAddress could perhaps call back with a result
     119             :             // synchronously, so do our state update first.
     120           0 :             MoveToState(State::ResolvingAddress);
     121           0 :             err = LookupPeerAddress();
     122           0 :             if (err != CHIP_NO_ERROR)
     123             :             {
     124             :                 // Roll back the state change, since we are presumably not in
     125             :                 // the middle of a lookup.
     126           0 :                 MoveToState(State::NeedsAddress);
     127             :             }
     128             :         }
     129             : 
     130           0 :         break;
     131             : 
     132           0 :     case State::ResolvingAddress:
     133             :     case State::WaitingForRetry:
     134           0 :         isConnected = AttachToExistingSecureSession();
     135           0 :         break;
     136             : 
     137           0 :     case State::HasAddress:
     138           0 :         isConnected = AttachToExistingSecureSession();
     139           0 :         if (!isConnected)
     140             :         {
     141             :             // We should not actually every be in be in State::HasAddress. This
     142             :             // is because in the same call that we moved to State::HasAddress
     143             :             // we either move to State::Connecting or call
     144             :             // DequeueConnectionCallbacks with an error thus releasing
     145             :             // ourselves before any call would reach this section of code.
     146           0 :             err = CHIP_ERROR_INCORRECT_STATE;
     147             :         }
     148             : 
     149           0 :         break;
     150             : 
     151           0 :     case State::Connecting:
     152           0 :         break;
     153             : 
     154           0 :     case State::SecureConnected:
     155           0 :         isConnected = true;
     156           0 :         break;
     157             : 
     158           0 :     default:
     159           0 :         err = CHIP_ERROR_INCORRECT_STATE;
     160             :     }
     161             : 
     162           0 :     if (isConnected)
     163             :     {
     164           0 :         MoveToState(State::SecureConnected);
     165             :     }
     166             : 
     167             :     //
     168             :     // Dequeue all our callbacks on either encountering an error
     169             :     // or if we successfully connected. Both should not be set
     170             :     // simultaneously.
     171             :     //
     172           0 :     if (err != CHIP_NO_ERROR || isConnected)
     173             :     {
     174           0 :         DequeueConnectionCallbacks(err);
     175             :         // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     176             :         // While it is odd to have an explicit return here at the end of the function, we do so
     177             :         // as a precaution in case someone later on adds something to the end of this function.
     178           0 :         return;
     179             :     }
     180             : }
     181             : 
     182           0 : void OperationalSessionSetup::Connect(Callback::Callback<OnDeviceConnected> * onConnection,
     183             :                                       Callback::Callback<OnDeviceConnectionFailure> * onFailure)
     184             : {
     185           0 :     Connect(onConnection, onFailure, nullptr);
     186           0 : }
     187             : 
     188           0 : void OperationalSessionSetup::Connect(Callback::Callback<OnDeviceConnected> * onConnection,
     189             :                                       Callback::Callback<OnSetupFailure> * onSetupFailure)
     190             : {
     191           0 :     Connect(onConnection, nullptr, onSetupFailure);
     192           0 : }
     193             : 
     194           0 : void OperationalSessionSetup::UpdateDeviceData(const Transport::PeerAddress & addr, const ReliableMessageProtocolConfig & config)
     195             : {
     196             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     197             :     // Make sure to clear out our reason for trying the next result first thing,
     198             :     // so it does not stick around in various error cases.
     199           0 :     bool tryingNextResultDueToSessionEstablishmentError = mTryingNextResultDueToSessionEstablishmentError;
     200           0 :     mTryingNextResultDueToSessionEstablishmentError     = false;
     201             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     202             : 
     203           0 :     if (mState == State::Uninitialized)
     204             :     {
     205           0 :         return;
     206             :     }
     207             : 
     208             : #if CHIP_DETAIL_LOGGING
     209             :     char peerAddrBuff[Transport::PeerAddress::kMaxToStringSize];
     210           0 :     addr.ToString(peerAddrBuff);
     211             : 
     212           0 :     ChipLogDetail(Discovery, "OperationalSessionSetup[%u:" ChipLogFormatX64 "]: Updating device address to %s while in state %d",
     213             :                   mPeerId.GetFabricIndex(), ChipLogValueX64(mPeerId.GetNodeId()), peerAddrBuff, static_cast<int>(mState));
     214             : #endif
     215             : 
     216           0 :     mDeviceAddress = addr;
     217             : 
     218             :     // Initialize CASE session state with any MRP parameters that DNS-SD has provided.
     219             :     // It can be overridden by CASE session protocol messages that include MRP parameters.
     220           0 :     if (mCASEClient)
     221             :     {
     222           0 :         mCASEClient->SetRemoteMRPIntervals(config);
     223             :     }
     224             : 
     225           0 :     if (mState != State::ResolvingAddress)
     226             :     {
     227           0 :         ChipLogError(Discovery, "Received UpdateDeviceData in incorrect state");
     228           0 :         DequeueConnectionCallbacks(CHIP_ERROR_INCORRECT_STATE);
     229             :         // Do not touch `this` instance anymore; it has been destroyed in
     230             :         // DequeueConnectionCallbacks.
     231           0 :         return;
     232             :     }
     233             : 
     234           0 :     MoveToState(State::HasAddress);
     235           0 :     mInitParams.sessionManager->UpdateAllSessionsPeerAddress(mPeerId, addr);
     236             : 
     237           0 :     if (mPerformingAddressUpdate)
     238             :     {
     239             :         // Nothing else to do here.
     240           0 :         DequeueConnectionCallbacks(CHIP_NO_ERROR);
     241             :         // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     242           0 :         return;
     243             :     }
     244             : 
     245           0 :     CHIP_ERROR err = EstablishConnection(config);
     246           0 :     LogErrorOnFailure(err);
     247           0 :     if (err == CHIP_NO_ERROR)
     248             :     {
     249             :         // We expect to get a callback via OnSessionEstablished or OnSessionEstablishmentError to continue
     250             :         // the state machine forward.
     251             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     252           0 :         if (tryingNextResultDueToSessionEstablishmentError)
     253             :         {
     254             :             // Our retry has already been kicked off, so claim 0 delay until it
     255             :             // starts.  We only reach this from OnSessionEstablishmentError when
     256             :             // the error is CHIP_ERROR_TIMEOUT.
     257           0 :             NotifyRetryHandlers(CHIP_ERROR_TIMEOUT, config, System::Clock::kZero);
     258             :         }
     259             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     260           0 :         return;
     261             :     }
     262             : 
     263             :     // Move to the ResolvingAddress state, in case we have more results,
     264             :     // since we expect to receive results in that state.  Pretend like we moved
     265             :     // on directly to this address from whatever triggered us to try this result
     266             :     // (so restore mTryingNextResultDueToSessionEstablishmentError to the value
     267             :     // it had at the start of this function).
     268           0 :     MoveToState(State::ResolvingAddress);
     269             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     270           0 :     mTryingNextResultDueToSessionEstablishmentError = tryingNextResultDueToSessionEstablishmentError;
     271             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     272           0 :     if (CHIP_NO_ERROR == Resolver::Instance().TryNextResult(mAddressLookupHandle))
     273             :     {
     274             :         // No need to NotifyRetryHandlers, since we never actually spent any
     275             :         // time trying the previous result.  Whatever work we need to do has
     276             :         // been handled by our recursive OnNodeAddressResolved callback.  Make
     277             :         // sure not to touch `this` under here, because it might have been
     278             :         // deleted by OnNodeAddressResolved.
     279           0 :         return;
     280             :     }
     281             : 
     282             :     // No need to reset mTryingNextResultDueToSessionEstablishmentError here,
     283             :     // because we're about to delete ourselves.
     284             : 
     285           0 :     DequeueConnectionCallbacks(err);
     286             :     // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     287             : }
     288             : 
     289           0 : CHIP_ERROR OperationalSessionSetup::EstablishConnection(const ReliableMessageProtocolConfig & config)
     290             : {
     291           0 :     mCASEClient = mClientPool->Allocate();
     292           0 :     ReturnErrorCodeIf(mCASEClient == nullptr, CHIP_ERROR_NO_MEMORY);
     293             : 
     294           0 :     CHIP_ERROR err = mCASEClient->EstablishSession(mInitParams, mPeerId, mDeviceAddress, config, this);
     295           0 :     if (err != CHIP_NO_ERROR)
     296             :     {
     297           0 :         CleanupCASEClient();
     298           0 :         return err;
     299             :     }
     300             : 
     301           0 :     MoveToState(State::Connecting);
     302             : 
     303           0 :     return CHIP_NO_ERROR;
     304             : }
     305             : 
     306           0 : void OperationalSessionSetup::EnqueueConnectionCallbacks(Callback::Callback<OnDeviceConnected> * onConnection,
     307             :                                                          Callback::Callback<OnDeviceConnectionFailure> * onFailure,
     308             :                                                          Callback::Callback<OnSetupFailure> * onSetupFailure)
     309             : {
     310           0 :     if (onConnection != nullptr)
     311             :     {
     312           0 :         mConnectionSuccess.Enqueue(onConnection->Cancel());
     313             :     }
     314             : 
     315           0 :     if (onFailure != nullptr)
     316             :     {
     317           0 :         mConnectionFailure.Enqueue(onFailure->Cancel());
     318             :     }
     319             : 
     320           0 :     if (onSetupFailure != nullptr)
     321             :     {
     322           0 :         mSetupFailure.Enqueue(onSetupFailure->Cancel());
     323             :     }
     324           0 : }
     325             : 
     326           0 : void OperationalSessionSetup::DequeueConnectionCallbacks(CHIP_ERROR error, SessionEstablishmentStage stage,
     327             :                                                          ReleaseBehavior releaseBehavior)
     328             : {
     329           0 :     Cancelable failureReady, setupFailureReady, successReady;
     330             : 
     331             :     //
     332             :     // Dequeue both failure and success callback lists into temporary stack args before invoking either of them.
     333             :     // We do this since we may not have a valid 'this' pointer anymore upon invoking any of those callbacks
     334             :     // since the callee may destroy this object as part of that callback.
     335             :     //
     336           0 :     mConnectionFailure.DequeueAll(failureReady);
     337           0 :     mSetupFailure.DequeueAll(setupFailureReady);
     338           0 :     mConnectionSuccess.DequeueAll(successReady);
     339             : 
     340             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     341             :     // Clear out mConnectionRetry, so that those cancelables are not holding
     342             :     // pointers to us, since we're about to go away.
     343           0 :     while (auto * cb = mConnectionRetry.First())
     344             :     {
     345           0 :         cb->Cancel();
     346           0 :     }
     347             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     348             : 
     349             :     // Gather up state we will need for our notifications.
     350           0 :     bool performingAddressUpdate                  = mPerformingAddressUpdate;
     351           0 :     auto * exchangeMgr                            = mInitParams.exchangeMgr;
     352           0 :     Optional<SessionHandle> optionalSessionHandle = mSecureSession.Get();
     353           0 :     ScopedNodeId peerId                           = mPeerId;
     354             : 
     355           0 :     if (releaseBehavior == ReleaseBehavior::Release)
     356             :     {
     357           0 :         VerifyOrDie(mReleaseDelegate != nullptr);
     358           0 :         mReleaseDelegate->ReleaseSession(this);
     359             :     }
     360             : 
     361             :     // DO NOT touch any members of this object after this point.  It's dead.
     362             : 
     363           0 :     NotifyConnectionCallbacks(failureReady, setupFailureReady, successReady, error, stage, peerId, performingAddressUpdate,
     364             :                               exchangeMgr, optionalSessionHandle);
     365           0 : }
     366             : 
     367           0 : void OperationalSessionSetup::NotifyConnectionCallbacks(Cancelable & failureReady, Cancelable & setupFailureReady,
     368             :                                                         Cancelable & successReady, CHIP_ERROR error,
     369             :                                                         SessionEstablishmentStage stage, const ScopedNodeId & peerId,
     370             :                                                         bool performingAddressUpdate, Messaging::ExchangeManager * exchangeMgr,
     371             :                                                         const Optional<SessionHandle> & optionalSessionHandle)
     372             : {
     373             :     //
     374             :     // If we encountered no error, go ahead and call all success callbacks. Otherwise,
     375             :     // call the failure callbacks.
     376             :     //
     377           0 :     while (failureReady.mNext != &failureReady)
     378             :     {
     379             :         // We expect that we only have callbacks if we are not performing just address update.
     380           0 :         VerifyOrDie(!performingAddressUpdate);
     381             :         Callback::Callback<OnDeviceConnectionFailure> * cb =
     382           0 :             Callback::Callback<OnDeviceConnectionFailure>::FromCancelable(failureReady.mNext);
     383             : 
     384           0 :         cb->Cancel();
     385             : 
     386           0 :         if (error != CHIP_NO_ERROR)
     387             :         {
     388           0 :             cb->mCall(cb->mContext, peerId, error);
     389             :         }
     390             :     }
     391             : 
     392           0 :     while (setupFailureReady.mNext != &setupFailureReady)
     393             :     {
     394             :         // We expect that we only have callbacks if we are not performing just address update.
     395           0 :         VerifyOrDie(!performingAddressUpdate);
     396           0 :         Callback::Callback<OnSetupFailure> * cb = Callback::Callback<OnSetupFailure>::FromCancelable(setupFailureReady.mNext);
     397             : 
     398           0 :         cb->Cancel();
     399             : 
     400           0 :         if (error != CHIP_NO_ERROR)
     401             :         {
     402             :             // Initialize the ConnnectionFailureInfo object
     403           0 :             ConnnectionFailureInfo failureInfo(peerId, error, stage);
     404           0 :             cb->mCall(cb->mContext, failureInfo);
     405             :         }
     406             :     }
     407             : 
     408           0 :     while (successReady.mNext != &successReady)
     409             :     {
     410             :         // We expect that we only have callbacks if we are not performing just address update.
     411           0 :         VerifyOrDie(!performingAddressUpdate);
     412           0 :         Callback::Callback<OnDeviceConnected> * cb = Callback::Callback<OnDeviceConnected>::FromCancelable(successReady.mNext);
     413             : 
     414           0 :         cb->Cancel();
     415           0 :         if (error == CHIP_NO_ERROR)
     416             :         {
     417           0 :             VerifyOrDie(exchangeMgr);
     418             :             // We know that we for sure have the SessionHandle in the successful case.
     419           0 :             cb->mCall(cb->mContext, *exchangeMgr, optionalSessionHandle.Value());
     420             :         }
     421             :     }
     422           0 : }
     423             : 
     424           0 : void OperationalSessionSetup::OnSessionEstablishmentError(CHIP_ERROR error, SessionEstablishmentStage stage)
     425             : {
     426           0 :     VerifyOrReturn(mState == State::Connecting,
     427             :                    ChipLogError(Discovery, "OnSessionEstablishmentError was called while we were not connecting"));
     428             : 
     429             :     // If this condition ever changes, we may need to store the error in a
     430             :     // member instead of having a boolean
     431             :     // mTryingNextResultDueToSessionEstablishmentError, so we can recover the
     432             :     // error in UpdateDeviceData.
     433           0 :     if (CHIP_ERROR_TIMEOUT == error)
     434             :     {
     435             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     436             :         // Make a copy of the ReliableMessageProtocolConfig, since our
     437             :         // mCaseClient is about to go away once we change state.
     438           0 :         ReliableMessageProtocolConfig remoteMprConfig = mCASEClient->GetRemoteMRPIntervals();
     439             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     440             : 
     441             :         // Move to the ResolvingAddress state, in case we have more results,
     442             :         // since we expect to receive results in that state.
     443           0 :         MoveToState(State::ResolvingAddress);
     444             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     445           0 :         mTryingNextResultDueToSessionEstablishmentError = true;
     446             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     447           0 :         if (CHIP_NO_ERROR == Resolver::Instance().TryNextResult(mAddressLookupHandle))
     448             :         {
     449             :             // Whatever work we needed to do has been handled by our
     450             :             // OnNodeAddressResolved callback.  Make sure not to touch `this`
     451             :             // under here, because it might have been deleted by
     452             :             // OnNodeAddressResolved.
     453           0 :             return;
     454             :         }
     455             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     456           0 :         mTryingNextResultDueToSessionEstablishmentError = false;
     457             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     458             : 
     459             :         // Moving back to the Connecting state would be a bit of a lie, since we
     460             :         // don't have an mCASEClient.  Just go back to NeedsAddress, since
     461             :         // that's really where we are now.
     462           0 :         MoveToState(State::NeedsAddress);
     463             : 
     464             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     465           0 :         if (mRemainingAttempts > 0)
     466             :         {
     467             :             System::Clock::Seconds16 reattemptDelay;
     468           0 :             CHIP_ERROR err = ScheduleSessionSetupReattempt(reattemptDelay);
     469           0 :             if (err == CHIP_NO_ERROR)
     470             :             {
     471           0 :                 MoveToState(State::WaitingForRetry);
     472           0 :                 NotifyRetryHandlers(error, remoteMprConfig, reattemptDelay);
     473           0 :                 return;
     474             :             }
     475             :         }
     476             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     477             :     }
     478             : 
     479           0 :     DequeueConnectionCallbacks(error, stage);
     480             :     // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     481             : }
     482             : 
     483           0 : void OperationalSessionSetup::OnSessionEstablished(const SessionHandle & session)
     484             : {
     485           0 :     VerifyOrReturn(mState == State::Connecting,
     486             :                    ChipLogError(Discovery, "OnSessionEstablished was called while we were not connecting"));
     487             : 
     488           0 :     if (!mSecureSession.Grab(session))
     489             :     {
     490             :         // Got an invalid session, just dispatch an error.  We have to do this
     491             :         // so we don't leak.
     492           0 :         DequeueConnectionCallbacks(CHIP_ERROR_INCORRECT_STATE);
     493             : 
     494             :         // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     495           0 :         return;
     496             :     }
     497             : 
     498           0 :     MoveToState(State::SecureConnected);
     499             : 
     500           0 :     DequeueConnectionCallbacks(CHIP_NO_ERROR);
     501             : }
     502             : 
     503           0 : void OperationalSessionSetup::CleanupCASEClient()
     504             : {
     505           0 :     if (mCASEClient)
     506             :     {
     507           0 :         mClientPool->Release(mCASEClient);
     508           0 :         mCASEClient = nullptr;
     509             :     }
     510           0 : }
     511             : 
     512           0 : OperationalSessionSetup::~OperationalSessionSetup()
     513             : {
     514           0 :     if (mAddressLookupHandle.IsActive())
     515             :     {
     516           0 :         ChipLogDetail(Discovery,
     517             :                       "OperationalSessionSetup[%u:" ChipLogFormatX64
     518             :                       "]: Cancelling incomplete address resolution as device is being deleted.",
     519             :                       mPeerId.GetFabricIndex(), ChipLogValueX64(mPeerId.GetNodeId()));
     520             : 
     521             :         // Skip cancel callback since the destructor is being called, so we assume that this object is
     522             :         // obviously not used anymore
     523           0 :         CHIP_ERROR err = Resolver::Instance().CancelLookup(mAddressLookupHandle, Resolver::FailureCallback::Skip);
     524           0 :         if (err != CHIP_NO_ERROR)
     525             :         {
     526           0 :             ChipLogError(Discovery, "Lookup cancel failed: %" CHIP_ERROR_FORMAT, err.Format());
     527             :         }
     528             :     }
     529             : 
     530           0 :     if (mCASEClient)
     531             :     {
     532             :         // Make sure we don't leak it.
     533           0 :         mClientPool->Release(mCASEClient);
     534             :     }
     535             : 
     536             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     537           0 :     CancelSessionSetupReattempt();
     538             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     539             : 
     540           0 :     DequeueConnectionCallbacks(CHIP_ERROR_CANCELLED, ReleaseBehavior::DoNotRelease);
     541           0 : }
     542             : 
     543           0 : CHIP_ERROR OperationalSessionSetup::LookupPeerAddress()
     544             : {
     545             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     546           0 :     if (mRemainingAttempts > 0)
     547             :     {
     548           0 :         --mRemainingAttempts;
     549             :     }
     550           0 :     if (mAttemptsDone < UINT8_MAX)
     551             :     {
     552           0 :         ++mAttemptsDone;
     553             :     }
     554           0 :     if (mResolveAttemptsAllowed > 0)
     555             :     {
     556           0 :         --mResolveAttemptsAllowed;
     557             :     }
     558             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     559             : 
     560             :     // NOTE: This is public API that can be used to update our stored peer
     561             :     // address even when we are in State::Connected, so we do not make any
     562             :     // MoveToState calls in this method.
     563           0 :     if (mAddressLookupHandle.IsActive())
     564             :     {
     565           0 :         ChipLogProgress(Discovery,
     566             :                         "OperationalSessionSetup[%u:" ChipLogFormatX64
     567             :                         "]: Operational node lookup already in progress. Will NOT start a new one.",
     568             :                         mPeerId.GetFabricIndex(), ChipLogValueX64(mPeerId.GetNodeId()));
     569           0 :         return CHIP_NO_ERROR;
     570             :     }
     571             : 
     572           0 :     auto const * fabricInfo = mInitParams.fabricTable->FindFabricWithIndex(mPeerId.GetFabricIndex());
     573           0 :     VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
     574             : 
     575           0 :     PeerId peerId(fabricInfo->GetCompressedFabricId(), mPeerId.GetNodeId());
     576             : 
     577           0 :     NodeLookupRequest request(peerId);
     578             : 
     579           0 :     return Resolver::Instance().LookupNode(request, mAddressLookupHandle);
     580             : }
     581             : 
     582           0 : void OperationalSessionSetup::PerformAddressUpdate()
     583             : {
     584           0 :     if (mPerformingAddressUpdate)
     585             :     {
     586             :         // We are already in the middle of a lookup from a previous call to
     587             :         // PerformAddressUpdate. In that case we will just exit right away as
     588             :         // we are already looking to update the results from the previous lookup.
     589           0 :         return;
     590             :     }
     591             : 
     592             :     // We must be newly-allocated to handle this address lookup, so must be in the NeedsAddress state.
     593           0 :     VerifyOrDie(mState == State::NeedsAddress);
     594             : 
     595             :     // We are doing an address lookup whether we have an active session for this peer or not.
     596           0 :     mPerformingAddressUpdate = true;
     597           0 :     MoveToState(State::ResolvingAddress);
     598           0 :     CHIP_ERROR err = LookupPeerAddress();
     599           0 :     if (err != CHIP_NO_ERROR)
     600             :     {
     601           0 :         ChipLogError(Discovery, "Failed to look up peer address: %" CHIP_ERROR_FORMAT, err.Format());
     602           0 :         DequeueConnectionCallbacks(err);
     603             :         // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     604           0 :         return;
     605             :     }
     606             : }
     607             : 
     608           0 : void OperationalSessionSetup::OnNodeAddressResolved(const PeerId & peerId, const ResolveResult & result)
     609             : {
     610           0 :     UpdateDeviceData(result.address, result.mrpRemoteConfig);
     611           0 : }
     612             : 
     613           0 : void OperationalSessionSetup::OnNodeAddressResolutionFailed(const PeerId & peerId, CHIP_ERROR reason)
     614             : {
     615           0 :     ChipLogError(Discovery, "OperationalSessionSetup[%u:" ChipLogFormatX64 "]: operational discovery failed: %" CHIP_ERROR_FORMAT,
     616             :                  mPeerId.GetFabricIndex(), ChipLogValueX64(mPeerId.GetNodeId()), reason.Format());
     617             : 
     618             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     619             :     // If we're in a mode where we would generally retry CASE, retry operational
     620             :     // discovery if we're allowed to.  That allows us to more-gracefully handle broken networks
     621             :     // where multicast DNS does not actually work and hence only the initial
     622             :     // unicast DNS-SD queries get a response.
     623             :     //
     624             :     // We check for State::ResolvingAddress just in case in the meantime
     625             :     // something weird happened and we are no longer trying to resolve an
     626             :     // address.
     627           0 :     if (mState == State::ResolvingAddress && mResolveAttemptsAllowed > 0)
     628             :     {
     629           0 :         ChipLogProgress(Discovery, "Retrying operational DNS-SD discovery. Attempts remaining: %u", mResolveAttemptsAllowed);
     630             : 
     631             :         // Pretend like our previous attempt (i.e. call to LookupPeerAddress)
     632             :         // has not happened for purposes of the generic attempt counters, so we
     633             :         // don't mess up the counters for our actual CASE retry logic.
     634           0 :         if (mRemainingAttempts < UINT8_MAX)
     635             :         {
     636           0 :             ++mRemainingAttempts;
     637             :         }
     638           0 :         if (mAttemptsDone > 0)
     639             :         {
     640           0 :             --mAttemptsDone;
     641             :         }
     642             : 
     643           0 :         CHIP_ERROR err = LookupPeerAddress();
     644           0 :         if (err == CHIP_NO_ERROR)
     645             :         {
     646             :             // We need to notify our consumer that the resolve will take more
     647             :             // time, but we don't actually know how much time it will take,
     648             :             // because the resolver does not expose that information.  Just use
     649             :             // one minute to be safe.
     650             :             using namespace chip::System::Clock::Literals;
     651           0 :             NotifyRetryHandlers(reason, 60_s16);
     652           0 :             return;
     653             :         }
     654             :     }
     655             : #endif
     656             : 
     657             :     // No need to modify any variables in `this` since call below releases `this`.
     658           0 :     DequeueConnectionCallbacks(reason);
     659             :     // Do not touch `this` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     660             : }
     661             : 
     662             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     663           0 : void OperationalSessionSetup::UpdateAttemptCount(uint8_t attemptCount)
     664             : {
     665           0 :     if (attemptCount == 0)
     666             :     {
     667             :         // Nothing to do.
     668           0 :         return;
     669             :     }
     670             : 
     671           0 :     if (mState != State::NeedsAddress)
     672             :     {
     673             :         // We're in the middle of an attempt already, so decrement attemptCount
     674             :         // by 1 to account for that.
     675           0 :         --attemptCount;
     676             :     }
     677             : 
     678           0 :     if (attemptCount > mRemainingAttempts)
     679             :     {
     680           0 :         mRemainingAttempts = attemptCount;
     681             :     }
     682             : 
     683           0 :     if (attemptCount > mResolveAttemptsAllowed)
     684             :     {
     685           0 :         mResolveAttemptsAllowed = attemptCount;
     686             :     }
     687             : }
     688             : 
     689           0 : CHIP_ERROR OperationalSessionSetup::ScheduleSessionSetupReattempt(System::Clock::Seconds16 & timerDelay)
     690             : {
     691           0 :     VerifyOrDie(mRemainingAttempts > 0);
     692             :     // Try again, but not if things are in shutdown such that we can't get
     693             :     // to a system layer, and not if we've run out of attempts.
     694           0 :     if (!mInitParams.exchangeMgr->GetSessionManager() || !mInitParams.exchangeMgr->GetSessionManager()->SystemLayer())
     695             :     {
     696           0 :         return CHIP_ERROR_INCORRECT_STATE;
     697             :     }
     698             : 
     699           0 :     MoveToState(State::NeedsAddress);
     700             :     // Stop exponential backoff before our delays get too large.
     701             :     //
     702             :     // Note that mAttemptsDone is always > 0 here, because we have
     703             :     // just finished one attempt.
     704           0 :     VerifyOrDie(mAttemptsDone > 0);
     705             :     static_assert(UINT16_MAX / CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_INITIAL_DELAY_SECONDS >=
     706             :                       (1 << CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_MAX_BACKOFF),
     707             :                   "Our backoff calculation will overflow.");
     708           0 :     timerDelay = System::Clock::Seconds16(
     709           0 :         static_cast<uint16_t>(CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_INITIAL_DELAY_SECONDS
     710           0 :                               << min((mAttemptsDone - 1), CHIP_DEVICE_CONFIG_AUTOMATIC_CASE_RETRY_MAX_BACKOFF)));
     711           0 :     if (mAttemptsDone % 2 == 0)
     712             :     {
     713             :         // It's possible that the other side received one of our Sigma1 messages
     714             :         // and then failed to get its Sigma2 back to us.  If that's the case, it
     715             :         // will be waiting for that Sigma2 to time out before it starts
     716             :         // listening for Sigma1 messages again.
     717             :         //
     718             :         // To handle that, on every other retry, add the amount of time it would
     719             :         // take the other side to time out.
     720           0 :         auto additionalTimeout = CASESession::ComputeSigma2ResponseTimeout(GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig()));
     721           0 :         timerDelay += std::chrono::duration_cast<System::Clock::Seconds16>(additionalTimeout);
     722             :     }
     723           0 :     CHIP_ERROR err = mInitParams.exchangeMgr->GetSessionManager()->SystemLayer()->StartTimer(timerDelay, TrySetupAgain, this);
     724             :     // The cast on count() is needed because the type count() returns might not
     725             :     // actually be uint16_t; on some platforms it's int.
     726           0 :     ChipLogProgress(Discovery,
     727             :                     "OperationalSessionSetup:attempts done: %u, attempts left: %u, retry delay %us, status %" CHIP_ERROR_FORMAT,
     728             :                     mAttemptsDone, mRemainingAttempts, static_cast<unsigned>(timerDelay.count()), err.Format());
     729           0 :     return err;
     730             : }
     731             : 
     732           0 : void OperationalSessionSetup::CancelSessionSetupReattempt()
     733             : {
     734             :     // If we can't get a system layer, there is no way for us to cancel things
     735             :     // at this point, but hopefully that's because everything is torn down
     736             :     // anyway and hence the timer will not fire.
     737           0 :     auto * sessionManager = mInitParams.exchangeMgr->GetSessionManager();
     738           0 :     VerifyOrReturn(sessionManager != nullptr);
     739             : 
     740           0 :     auto * systemLayer = sessionManager->SystemLayer();
     741           0 :     VerifyOrReturn(systemLayer != nullptr);
     742             : 
     743           0 :     systemLayer->CancelTimer(TrySetupAgain, this);
     744             : }
     745             : 
     746           0 : void OperationalSessionSetup::TrySetupAgain(System::Layer * systemLayer, void * state)
     747             : {
     748           0 :     auto * self = static_cast<OperationalSessionSetup *>(state);
     749             : 
     750           0 :     self->MoveToState(State::ResolvingAddress);
     751           0 :     CHIP_ERROR err = self->LookupPeerAddress();
     752           0 :     if (err == CHIP_NO_ERROR)
     753             :     {
     754           0 :         return;
     755             :     }
     756             : 
     757             :     // Give up; we could not start a lookup.
     758           0 :     self->DequeueConnectionCallbacks(err);
     759             :     // Do not touch `self` instance anymore; it has been destroyed in DequeueConnectionCallbacks.
     760             : }
     761             : 
     762           0 : void OperationalSessionSetup::AddRetryHandler(Callback::Callback<OnDeviceConnectionRetry> * onRetry)
     763             : {
     764           0 :     mConnectionRetry.Enqueue(onRetry->Cancel());
     765           0 : }
     766             : 
     767           0 : void OperationalSessionSetup::NotifyRetryHandlers(CHIP_ERROR error, const ReliableMessageProtocolConfig & remoteMrpConfig,
     768             :                                                   System::Clock::Seconds16 retryDelay)
     769             : {
     770             :     // Compute the time we are likely to need to detect that the retry has
     771             :     // failed.
     772           0 :     System::Clock::Timeout messageTimeout = CASESession::ComputeSigma1ResponseTimeout(remoteMrpConfig);
     773           0 :     auto timeoutSecs                      = std::chrono::duration_cast<System::Clock::Seconds16>(messageTimeout);
     774             :     // Add 1 second in case we had fractional milliseconds in messageTimeout.
     775             :     using namespace chip::System::Clock::Literals;
     776           0 :     NotifyRetryHandlers(error, timeoutSecs + 1_s16 + retryDelay);
     777           0 : }
     778             : 
     779           0 : void OperationalSessionSetup::NotifyRetryHandlers(CHIP_ERROR error, System::Clock::Seconds16 timeoutEstimate)
     780             : {
     781             :     // We have to be very careful here: Calling into these handlers might in
     782             :     // theory destroy the Callback objects involved, but unlike the
     783             :     // succcess/failure cases we don't want to just clear the handlers from our
     784             :     // list when we are calling them, because we might need to call a given
     785             :     // handler more than once.
     786             :     //
     787             :     // To handle this we:
     788             :     //
     789             :     // 1) Snapshot the list of handlers up front, so if any of the handlers
     790             :     //    triggers an AddRetryHandler with some other handler that does not
     791             :     //    affect the list we plan to notify here.
     792             :     //
     793             :     // 2) When planning to notify a handler move it to a new list that contains
     794             :     //    just that handler.  This way if it gets canceled as part of the
     795             :     //    notification we can tell it has been canceled.
     796             :     //
     797             :     // 3) If notifying the handler does not cancel it, add it back to our list
     798             :     //    of handlers so we will notify it on future retries.
     799             : 
     800           0 :     Cancelable retryHandlerListSnapshot;
     801           0 :     mConnectionRetry.DequeueAll(retryHandlerListSnapshot);
     802             : 
     803           0 :     while (retryHandlerListSnapshot.mNext != &retryHandlerListSnapshot)
     804             :     {
     805           0 :         auto * cb = Callback::Callback<OnDeviceConnectionRetry>::FromCancelable(retryHandlerListSnapshot.mNext);
     806             : 
     807           0 :         Callback::CallbackDeque currentCallbackHolder;
     808           0 :         currentCallbackHolder.Enqueue(cb->Cancel());
     809             : 
     810           0 :         cb->mCall(cb->mContext, mPeerId, error, timeoutEstimate);
     811             : 
     812           0 :         if (currentCallbackHolder.mNext != &currentCallbackHolder)
     813             :         {
     814             :             // Callback has not been canceled as part of the call, so is still
     815             :             // supposed to be registered with us.
     816           0 :             AddRetryHandler(cb);
     817             :         }
     818           0 :     }
     819           0 : }
     820             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     821             : 
     822             : } // namespace chip

Generated by: LCOV version 1.14