Skip to content

Commit ae41431

Browse files
Add a suspend/resume API on MTRDeviceController. (project-chip#35434)
This allows controllers to be suspended temporarily without having to shut them down completely and restart them.
1 parent 7219ad2 commit ae41431

9 files changed

+104
-21
lines changed

src/darwin/Framework/CHIP/MTRDeviceController.h

+23
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1))
6565
*/
6666
@property (readonly, nonatomic, getter=isRunning) BOOL running;
6767

68+
/**
69+
* If true, the controller has been suspended via `suspend` and not resumed yet.
70+
*/
71+
@property (readonly, nonatomic, getter=isSuspended) BOOL suspended MTR_NEWLY_AVAILABLE;
72+
6873
/**
6974
* The ID assigned to this controller at creation time.
7075
*/
@@ -253,6 +258,24 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1))
253258
error:(NSError * __autoreleasing *)error
254259
MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4));
255260

261+
/**
262+
* Suspend the controller. This will attempt to stop all network traffic associated
263+
* with the controller. The controller will remain suspended until it is
264+
* resumed.
265+
*
266+
* Suspending an already-suspended controller has no effect.
267+
*/
268+
- (void)suspend MTR_NEWLY_AVAILABLE;
269+
270+
/**
271+
* Resume the controller. This has no effect if the controller is not
272+
* suspended.
273+
*
274+
* A resume following any number of suspend calls will resume the controller;
275+
* there does not need to be a resume call to match every suspend call.
276+
*/
277+
- (void)resume MTR_NEWLY_AVAILABLE;
278+
256279
/**
257280
* Shut down the controller. Calls to shutdown after the first one are NO-OPs.
258281
* This must be called, either directly or via shutting down the

src/darwin/Framework/CHIP/MTRDeviceController.mm

+38-3
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ @implementation MTRDeviceController {
131131
MTRP256KeypairBridge _signingKeypairBridge;
132132
MTRP256KeypairBridge _operationalKeypairBridge;
133133

134+
BOOL _suspended;
135+
134136
// Counters to track assertion status and access controlled by the _assertionLock
135137
NSUInteger _keepRunningAssertionCounter;
136138
BOOL _shutdownPending;
@@ -142,7 +144,7 @@ - (os_unfair_lock_t)deviceMapLock
142144
return &_underlyingDeviceMapLock;
143145
}
144146

145-
- (instancetype)initForSubclasses
147+
- (instancetype)initForSubclasses:(BOOL)startSuspended
146148
{
147149
if (self = [super init]) {
148150
// nothing, as superclass of MTRDeviceController is NSObject
@@ -153,15 +155,17 @@ - (instancetype)initForSubclasses
153155
_keepRunningAssertionCounter = 0;
154156
_shutdownPending = NO;
155157
_assertionLock = OS_UNFAIR_LOCK_INIT;
158+
159+
_suspended = startSuspended;
160+
156161
return self;
157162
}
158163

159164
- (nullable MTRDeviceController *)initWithParameters:(MTRDeviceControllerAbstractParameters *)parameters error:(NSError * __autoreleasing *)error
160165
{
161166
if ([parameters isKindOfClass:MTRXPCDeviceControllerParameters.class]) {
162-
MTRXPCDeviceControllerParameters * resolvedParameters = (MTRXPCDeviceControllerParameters *) parameters;
163167
MTR_LOG("Starting up with XPC Device Controller Parameters: %@", parameters);
164-
return [[MTRDeviceController_XPC alloc] initWithUniqueIdentifier:resolvedParameters.uniqueIdentifier xpConnectionBlock:resolvedParameters.xpcConnectionBlock];
168+
return [[MTRDeviceController_XPC alloc] initWithParameters:parameters error:error];
165169
} else if (![parameters isKindOfClass:MTRDeviceControllerParameters.class]) {
166170
MTR_LOG_ERROR("Unsupported type of MTRDeviceControllerAbstractParameters: %@", parameters);
167171
if (error) {
@@ -184,6 +188,7 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory
184188
uniqueIdentifier:(NSUUID *)uniqueIdentifier
185189
concurrentSubscriptionPoolSize:(NSUInteger)concurrentSubscriptionPoolSize
186190
storageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration *)storageBehaviorConfiguration
191+
startSuspended:(BOOL)startSuspended
187192
{
188193
if (self = [super init]) {
189194
// Make sure our storage is all set up to work as early as possible,
@@ -195,6 +200,8 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory
195200
_shutdownPending = NO;
196201
_assertionLock = OS_UNFAIR_LOCK_INIT;
197202

203+
_suspended = startSuspended;
204+
198205
if (storageDelegate != nil) {
199206
if (storageDelegateQueue == nil) {
200207
MTR_LOG_ERROR("storageDelegate provided without storageDelegateQueue");
@@ -331,6 +338,34 @@ - (BOOL)isRunning
331338
return _cppCommissioner != nullptr;
332339
}
333340

341+
#pragma mark - Suspend/resume support
342+
343+
- (BOOL)isSuspended
344+
{
345+
return _suspended;
346+
}
347+
348+
- (void)suspend
349+
{
350+
_suspended = YES;
351+
352+
// TODO: In the concrete class (which is unused so far!), iterate our
353+
// MTRDevices, tell them to tear down subscriptions. Possibly close all
354+
// CASE sessions for our identity. Possibly try to see whether we can
355+
// change our fabric entry to not advertise and restart advertising.
356+
357+
// TODO: What should happen with active commissioning sessions? Presumably
358+
// close them?
359+
}
360+
361+
- (void)resume
362+
{
363+
_suspended = NO;
364+
365+
// TODO: In the concrete class (which is unused so far!), iterate our
366+
// MTRDevices, tell them to restart subscriptions.
367+
}
368+
334369
- (BOOL)matchesPendingShutdownControllerWithOperationalCertificate:(nullable MTRCertificateDERBytes)operationalCertificate andRootCertificate:(nullable MTRCertificateDERBytes)rootCertificate
335370
{
336371
if (!operationalCertificate || !rootCertificate) {

src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm

+4-1
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ - (MTRDeviceController * _Nullable)_startDeviceController:(MTRDeviceController *
474474
dispatch_queue_t _Nullable otaProviderDelegateQueue;
475475
NSUInteger concurrentSubscriptionPoolSize = 0;
476476
MTRDeviceStorageBehaviorConfiguration * storageBehaviorConfiguration = nil;
477+
BOOL startSuspended = NO;
477478
if ([startupParams isKindOfClass:[MTRDeviceControllerParameters class]]) {
478479
MTRDeviceControllerParameters * params = startupParams;
479480
storageDelegate = params.storageDelegate;
@@ -483,6 +484,7 @@ - (MTRDeviceController * _Nullable)_startDeviceController:(MTRDeviceController *
483484
otaProviderDelegateQueue = params.otaProviderDelegateQueue;
484485
concurrentSubscriptionPoolSize = params.concurrentSubscriptionEstablishmentsAllowedOnThread;
485486
storageBehaviorConfiguration = params.storageBehaviorConfiguration;
487+
startSuspended = params.startSuspended;
486488
} else if ([startupParams isKindOfClass:[MTRDeviceControllerStartupParams class]]) {
487489
MTRDeviceControllerStartupParams * params = startupParams;
488490
storageDelegate = nil;
@@ -545,7 +547,8 @@ - (MTRDeviceController * _Nullable)_startDeviceController:(MTRDeviceController *
545547
otaProviderDelegateQueue:otaProviderDelegateQueue
546548
uniqueIdentifier:uniqueIdentifier
547549
concurrentSubscriptionPoolSize:concurrentSubscriptionPoolSize
548-
storageBehaviorConfiguration:storageBehaviorConfiguration];
550+
storageBehaviorConfiguration:storageBehaviorConfiguration
551+
startSuspended:startSuspended];
549552
if (controller == nil) {
550553
if (error != nil) {
551554
*error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_ARGUMENT];

src/darwin/Framework/CHIP/MTRDeviceControllerParameters.h

+7
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6))
3131
@interface MTRDeviceControllerAbstractParameters : NSObject
3232
- (instancetype)init NS_UNAVAILABLE;
3333
+ (instancetype)new NS_UNAVAILABLE;
34+
35+
/**
36+
* Whether the controller should start out suspended.
37+
*
38+
* Defaults to NO.
39+
*/
40+
@property (nonatomic, assign) BOOL startSuspended;
3441
@end
3542

3643
/**

src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm

+7-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,13 @@ - (instancetype)initWithOperationalKeypair:(id<MTRKeypair>)operationalKeypair
250250
@implementation MTRDeviceControllerAbstractParameters
251251
- (instancetype)_initInternal
252252
{
253-
return [super init];
253+
if (!(self = [super init])) {
254+
return nil;
255+
}
256+
257+
_startSuspended = NO;
258+
259+
return self;
254260
}
255261
@end
256262

src/darwin/Framework/CHIP/MTRDeviceController_Concrete.mm

+2-1
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,9 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory
190190
uniqueIdentifier:(NSUUID *)uniqueIdentifier
191191
concurrentSubscriptionPoolSize:(NSUInteger)concurrentSubscriptionPoolSize
192192
storageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration *)storageBehaviorConfiguration
193+
startSuspended:(BOOL)startSuspended
193194
{
194-
if (self = [super initForSubclasses]) {
195+
if (self = [super initForSubclasses:startSuspended]) {
195196
// Make sure our storage is all set up to work as early as possible,
196197
// before we start doing anything else with the controller.
197198
_uniqueIdentifier = uniqueIdentifier;

src/darwin/Framework/CHIP/MTRDeviceController_Internal.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ NS_ASSUME_NONNULL_BEGIN
7373
// (moved here so subclasses can initialize differently)
7474
@property (readwrite, retain) dispatch_queue_t chipWorkQueue;
7575

76-
- (instancetype)initForSubclasses;
76+
- (instancetype)initForSubclasses:(BOOL)startSuspended;
7777

7878
#pragma mark - MTRDeviceControllerFactory methods
7979

@@ -146,7 +146,8 @@ NS_ASSUME_NONNULL_BEGIN
146146
otaProviderDelegateQueue:(dispatch_queue_t _Nullable)otaProviderDelegateQueue
147147
uniqueIdentifier:(NSUUID *)uniqueIdentifier
148148
concurrentSubscriptionPoolSize:(NSUInteger)concurrentSubscriptionPoolSize
149-
storageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration *)storageBehaviorConfiguration;
149+
storageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration *)storageBehaviorConfiguration
150+
startSuspended:(BOOL)startSuspended;
150151

151152
/**
152153
* Check whether this controller is running on the given fabric, as represented

src/darwin/Framework/CHIP/MTRDeviceController_XPC.h

-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ NS_ASSUME_NONNULL_BEGIN
2323

2424
MTR_TESTABLE
2525
@interface MTRDeviceController_XPC : MTRDeviceController <MTRXPCClientProtocol>
26-
- (id)initWithUniqueIdentifier:(NSUUID *)UUID xpConnectionBlock:(NSXPCConnection * (^)(void) )connectionBlock;
2726
#ifdef MTR_HAVE_MACH_SERVICE_NAME_CONSTRUCTOR
2827
- (id)initWithUniqueIdentifier:(NSUUID *)UUID machServiceName:(NSString *)machServiceName options:(NSXPCConnectionOptions)options
2928
#endif

src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm

+20-12
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,30 @@ - (NSXPCInterface *)_interfaceForClientProtocol
8383
return [self.uniqueIdentifier UUIDString];
8484
}
8585

86-
- (id)initWithUniqueIdentifier:(NSUUID *)UUID xpConnectionBlock:(NSXPCConnection * (^)(void) )connectionBlock
86+
- (nullable instancetype)initWithParameters:(MTRDeviceControllerAbstractParameters *)parameters
87+
error:(NSError * __autoreleasing *)error
8788
{
88-
if (self = [super initForSubclasses]) {
89+
if (![parameters isKindOfClass:MTRXPCDeviceControllerParameters.class]) {
90+
MTR_LOG_ERROR("Expected MTRXPCDeviceControllerParameters but got: %@", parameters);
91+
if (error) {
92+
*error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_ARGUMENT];
93+
}
94+
return nil;
95+
}
96+
97+
if (self = [super initForSubclasses:parameters.startSuspended]) {
98+
auto * xpcParameters = static_cast<MTRXPCDeviceControllerParameters *>(parameters);
99+
auto connectionBlock = xpcParameters.xpcConnectionBlock;
100+
auto * UUID = xpcParameters.uniqueIdentifier;
101+
89102
MTR_LOG("Setting up XPC Controller for UUID: %@ with connection block: %p", UUID, connectionBlock);
90103

91104
if (UUID == nil) {
92-
MTR_LOG_ERROR("MTRDeviceController_XPC initWithUniqueIdentifier failed, nil UUID");
105+
MTR_LOG_ERROR("MTRDeviceController_XPC initWithParameters failed, nil UUID");
93106
return nil;
94107
}
95108
if (connectionBlock == nil) {
96-
MTR_LOG_ERROR("MTRDeviceController_XPC initWithUniqueIdentifier failed, nil connectionBlock");
109+
MTR_LOG_ERROR("MTRDeviceController_XPC initWithParameters failed, nil connectionBlock");
97110
return nil;
98111
}
99112

@@ -131,7 +144,9 @@ - (id)initWithUniqueIdentifier:(NSUUID *)UUID xpConnectionBlock:(NSXPCConnection
131144
#ifdef MTR_HAVE_MACH_SERVICE_NAME_CONSTRUCTOR
132145
- (id)initWithUniqueIdentifier:(NSUUID *)UUID machServiceName:(NSString *)machServiceName options:(NSXPCConnectionOptions)options
133146
{
134-
if (self = [super initForSubclasses]) {
147+
// TODO: Presumably this should end up doing some sort of
148+
// MTRDeviceControllerAbstractParameters thing eventually?
149+
if (self = [super initForSubclasses:NO]) {
135150
MTR_LOG("Setting up XPC Controller for UUID: %@ with machServiceName: %s options: %d", UUID, machServiceName, options);
136151
self.xpcConnection = [[NSXPCConnection alloc] initWithMachServiceName:machServiceName options:options];
137152
self.uniqueIdentifier = UUID;
@@ -155,13 +170,6 @@ - (id)initWithUniqueIdentifier:(NSUUID *)UUID machServiceName:(NSString *)machSe
155170
}
156171
#endif // MTR_HAVE_MACH_SERVICE_NAME_CONSTRUCTOR
157172

158-
- (nullable instancetype)initWithParameters:(MTRDeviceControllerAbstractParameters *)parameters
159-
error:(NSError * __autoreleasing *)error
160-
{
161-
MTR_LOG_ERROR("%s: unimplemented method called", __PRETTY_FUNCTION__);
162-
return nil;
163-
}
164-
165173
// If prefetchedClusterData is not provided, load attributes individually from controller data store
166174
- (MTRDevice *)_setupDeviceForNodeID:(NSNumber *)nodeID prefetchedClusterData:(NSDictionary<MTRClusterPath *, MTRDeviceClusterData *> *)prefetchedClusterData
167175
{

0 commit comments

Comments
 (0)