Skip to content

Commit 4f93a66

Browse files
authored
[Darwin] MTRDevice should trigger resubscription on connectivity changes (#33016)
* [Darwin] MTRDevice should trigger resubscription on connectivity changes * Address review comments and add unit test
1 parent fbe1835 commit 4f93a66

7 files changed

+460
-0
lines changed

src/darwin/Framework/CHIP/MTRDevice.mm

+51
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#import "MTRCommandTimedCheck.h"
2929
#import "MTRConversion.h"
3030
#import "MTRDefines_Internal.h"
31+
#import "MTRDeviceConnectivityMonitor.h"
3132
#import "MTRDeviceControllerOverXPC.h"
3233
#import "MTRDeviceController_Internal.h"
3334
#import "MTRDevice_Internal.h"
@@ -355,6 +356,7 @@ @implementation MTRDevice {
355356
// _setupSubscription or via the auto-resubscribe behavior of the
356357
// ReadClient). Nil if we have had no such failures.
357358
NSDate * _Nullable _lastSubscriptionFailureTime;
359+
MTRDeviceConnectivityMonitor * _connectivityMonitor;
358360
}
359361

360362
- (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller
@@ -669,6 +671,8 @@ - (void)invalidate
669671
// subscription. In that case, _internalDeviceState will update when the
670672
// subscription is actually terminated.
671673

674+
[self _stopConnectivityMonitoring];
675+
672676
os_unfair_lock_unlock(&self->_lock);
673677
}
674678

@@ -861,6 +865,9 @@ - (void)_handleSubscriptionEstablished
861865

862866
[self _changeState:MTRDeviceStateReachable];
863867

868+
// No need to monitor connectivity after subscription establishment
869+
[self _stopConnectivityMonitoring];
870+
864871
os_unfair_lock_unlock(&self->_lock);
865872

866873
os_unfair_lock_lock(&self->_timeSyncLock);
@@ -894,6 +901,9 @@ - (void)_handleResubscriptionNeeded
894901
// former case we recently had a subscription and do not want to be forcing
895902
// retries immediately.
896903
_lastSubscriptionFailureTime = [NSDate now];
904+
905+
// Set up connectivity monitoring in case network routability changes for the positive, to accellerate resubscription
906+
[self _setupConnectivityMonitoring];
897907
}
898908

899909
- (void)_handleSubscriptionReset:(NSNumber * _Nullable)retryDelay
@@ -1241,6 +1251,44 @@ - (void)_createDataVersionFilterListFromDictionary:(NSDictionary<MTRClusterPath
12411251
*count = maxDataVersionFilterSize;
12421252
}
12431253

1254+
- (void)_setupConnectivityMonitoring
1255+
{
1256+
// Dispatch to own queue first to avoid deadlock with syncGetCompressedFabricID
1257+
dispatch_async(self.queue, ^{
1258+
// Get the required info before setting up the connectivity monitor
1259+
NSNumber * compressedFabricID = [self->_deviceController syncGetCompressedFabricID];
1260+
if (!compressedFabricID) {
1261+
MTR_LOG_INFO("%@ could not get compressed fabricID", self);
1262+
return;
1263+
}
1264+
1265+
// Now lock for _connectivityMonitor
1266+
std::lock_guard lock(self->_lock);
1267+
if (self->_connectivityMonitor) {
1268+
// already monitoring
1269+
return;
1270+
}
1271+
1272+
self->_connectivityMonitor = [[MTRDeviceConnectivityMonitor alloc] initWithCompressedFabricID:compressedFabricID nodeID:self.nodeID];
1273+
[self->_connectivityMonitor startMonitoringWithHandler:^{
1274+
[self->_deviceController asyncDispatchToMatterQueue:^{
1275+
[self _triggerResubscribeWithReason:"read-through skipped while not subscribed" nodeLikelyReachable:YES];
1276+
}
1277+
errorHandler:nil];
1278+
} queue:self.queue];
1279+
});
1280+
}
1281+
1282+
- (void)_stopConnectivityMonitoring
1283+
{
1284+
os_unfair_lock_assert_owner(&_lock);
1285+
1286+
if (_connectivityMonitor) {
1287+
[_connectivityMonitor stopMonitoring];
1288+
_connectivityMonitor = nil;
1289+
}
1290+
}
1291+
12441292
// assume lock is held
12451293
- (void)_setupSubscription
12461294
{
@@ -1462,6 +1510,9 @@ - (void)_setupSubscription
14621510
callback->AdoptClusterStateCache(std::move(clusterStateCache));
14631511
callback.release();
14641512
}];
1513+
1514+
// Set up connectivity monitoring in case network becomes routable after any part of the subscription process goes into backoff retries.
1515+
[self _setupConnectivityMonitoring];
14651516
}
14661517

14671518
#ifdef DEBUG
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Copyright (c) 2023 Project CHIP Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
#import "MTRDefines_Internal.h"
20+
21+
NS_ASSUME_NONNULL_BEGIN
22+
23+
typedef void (^MTRDeviceConnectivityMonitorHandler)(void);
24+
25+
/**
26+
* Class that a matter dns-sd instance name, and monitors connectivity to the device.
27+
*/
28+
MTR_TESTABLE
29+
@interface MTRDeviceConnectivityMonitor : NSObject
30+
- (instancetype)initWithCompressedFabricID:(NSNumber *)compressedFabricID nodeID:(NSNumber *)nodeID;
31+
32+
/**
33+
* Any time a path becomes satisfied or route becomes viable, the registered handler will be called.
34+
*/
35+
- (void)startMonitoringWithHandler:(MTRDeviceConnectivityMonitorHandler)handler queue:(dispatch_queue_t)queue;
36+
37+
/**
38+
* Stops the monitoring. After this method returns no more calls to the handler will be made.
39+
*/
40+
- (void)stopMonitoring;
41+
@end
42+
43+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)