@@ -871,13 +871,7 @@ - (void)invalidate
871
871
// tear down the subscription. We will get no more callbacks from
872
872
// the subscription after this point.
873
873
std::lock_guard lock (self->_lock );
874
- self->_currentReadClient = nullptr ;
875
- if (self->_currentSubscriptionCallback ) {
876
- delete self->_currentSubscriptionCallback ;
877
- }
878
- self->_currentSubscriptionCallback = nullptr ;
879
-
880
- [self _changeInternalState: MTRInternalDeviceStateUnsubscribed];
874
+ [self _resetSubscription ];
881
875
}
882
876
errorHandler: nil ];
883
877
@@ -938,8 +932,8 @@ - (void)_triggerResubscribeWithReason:(NSString *)reason nodeLikelyReachable:(BO
938
932
}
939
933
readClientToResubscribe->TriggerResubscribeIfScheduled (reason.UTF8String );
940
934
} else if (_internalDeviceState == MTRInternalDeviceStateSubscribing && nodeLikelyReachable) {
941
- // If we have reason to suspect that the node is now reachable and we haven’ t established a
942
- // CASE session yet, let’ s consider it to be stalled and invalidate the pairing session.
935
+ // If we have reason to suspect that the node is now reachable and we haven' t established a
936
+ // CASE session yet, let' s consider it to be stalled and invalidate the pairing session.
943
937
[self ._concreteController asyncGetCommissionerOnMatterQueue: ^(Controller: :DeviceCommissioner * commissioner) {
944
938
auto caseSessionMgr = commissioner->CASESessionMgr ();
945
939
VerifyOrDie (caseSessionMgr != nullptr );
@@ -1164,7 +1158,7 @@ - (void)_handleSubscriptionError:(NSError *)error
1164
1158
[self _doHandleSubscriptionError: error];
1165
1159
}
1166
1160
1167
- - (void )_doHandleSubscriptionError : (NSError *)error
1161
+ - (void )_doHandleSubscriptionError : (nullable NSError *)error
1168
1162
{
1169
1163
assertChipStackLockedByCurrentThread ();
1170
1164
@@ -1305,7 +1299,13 @@ - (void)_handleResubscriptionNeededWithDelay:(NSNumber *)resubscriptionDelayMs
1305
1299
1306
1300
// Change our state before going async.
1307
1301
[self _changeState: MTRDeviceStateUnknown];
1308
- [self _changeInternalState: MTRInternalDeviceStateResubscribing];
1302
+
1303
+ // If we have never had a subscription established, stay in the Subscribing
1304
+ // state; don't transition to Resubscribing just because our attempt at
1305
+ // subscribing failed.
1306
+ if (HadSubscriptionEstablishedOnce (self->_internalDeviceState )) {
1307
+ [self _changeInternalState: MTRInternalDeviceStateResubscribing];
1308
+ }
1309
1309
1310
1310
dispatch_async (self.queue , ^{
1311
1311
[self _handleResubscriptionNeededWithDelayOnDeviceQueue: resubscriptionDelayMs];
@@ -2444,19 +2444,29 @@ - (void)_resetSubscriptionWithReasonString:(NSString *)reasonString
2444
2444
MTR_LOG (" %@ subscription reset disconnecting ReadClient and SubscriptionCallback" , self);
2445
2445
2446
2446
std::lock_guard lock (self->_lock );
2447
- self->_currentReadClient = nullptr ;
2448
- if (self->_currentSubscriptionCallback ) {
2449
- delete self->_currentSubscriptionCallback ;
2450
- }
2451
- self->_currentSubscriptionCallback = nullptr ;
2452
2447
2453
- [self _doHandleSubscriptionError: nil ];
2448
+ [self _resetSubscription ];
2449
+
2454
2450
// Use nil reset delay so that this keeps existing backoff timing
2455
2451
[self _doHandleSubscriptionReset: nil ];
2456
2452
}
2457
2453
errorHandler: nil ];
2458
2454
}
2459
2455
2456
+ - (void )_resetSubscription
2457
+ {
2458
+ assertChipStackLockedByCurrentThread ();
2459
+ os_unfair_lock_assert_owner (&_lock);
2460
+
2461
+ _currentReadClient = nullptr ;
2462
+ if (_currentSubscriptionCallback) {
2463
+ delete _currentSubscriptionCallback;
2464
+ _currentSubscriptionCallback = nullptr ;
2465
+ }
2466
+
2467
+ [self _doHandleSubscriptionError: nil ];
2468
+ }
2469
+
2460
2470
#ifdef DEBUG
2461
2471
- (void )unitTestResetSubscription
2462
2472
{
@@ -4322,11 +4332,31 @@ - (BOOL)_deviceHasActiveSubscription
4322
4332
4323
4333
- (void )_deviceMayBeReachable
4324
4334
{
4325
- MTR_LOG (" %@ _deviceMayBeReachable called" , self);
4335
+ MTR_LOG (" %@ _deviceMayBeReachable called, resetting subscription " , self);
4326
4336
// TODO: This should only be allowed for thread devices
4327
- [_deviceController asyncDispatchToMatterQueue: ^{
4328
- [self _triggerResubscribeWithReason: @" SPI client indicated the device may now be reachable"
4329
- nodeLikelyReachable: YES ];
4337
+ [self ._concreteController asyncGetCommissionerOnMatterQueue: ^(Controller: :DeviceCommissioner * commissioner) {
4338
+ // Reset all of our subscription/session state and re-establish it all
4339
+ // from the start. Reset our subscription first, before tearing
4340
+ // down the session, so we don't have to worry about the
4341
+ // notifications from the latter coming through async and
4342
+ // complicating the situation. Unfortunately, we do not want to
4343
+ // hold the lock when destroying the session, just in case it still
4344
+ // ends up calling into us somehow, so we have to break the work up
4345
+ // into two separate locked sections...
4346
+ {
4347
+ std::lock_guard lock (self->_lock );
4348
+ [self _clearSubscriptionPoolWork ];
4349
+ [self _resetSubscription ];
4350
+ }
4351
+
4352
+ auto caseSessionMgr = commissioner->CASESessionMgr ();
4353
+ VerifyOrDie (caseSessionMgr != nullptr );
4354
+ caseSessionMgr->ReleaseSession (commissioner->GetPeerScopedId (self->_nodeID .unsignedLongLongValue ));
4355
+
4356
+ std::lock_guard lock (self->_lock );
4357
+ // Use _ensureSubscriptionForExistingDelegates so that the subscriptions
4358
+ // will go through the pool as needed, not necessarily happen immediately.
4359
+ [self _ensureSubscriptionForExistingDelegates: @" SPI client indicated the device may now be reachable" ];
4330
4360
} errorHandler: nil ];
4331
4361
}
4332
4362
0 commit comments