Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating XPC interfaces to pass along context, and fixing some retries #35441

Merged
merged 5 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/darwin/Framework/CHIP/MTRDefines_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,56 @@ typedef struct {} variable_hidden_by_mtr_hide;
\
return outValue; \
}

#ifndef MTR_OPTIONAL_ATTRIBUTE
#if __has_feature(objc_arc)
#define MTR_OPTIONAL_ATTRIBUTE(ATTRIBUTE, VALUE, DICTIONARY) \
{ \
id valueToAdd = VALUE; \
if (valueToAdd != nil) { \
CFDictionarySetValue((CFMutableDictionaryRef) DICTIONARY, (CFStringRef) (__bridge const void *) ATTRIBUTE, (const void *) valueToAdd); \
} \
}
#else
#define MTR_OPTIONAL_ATTRIBUTE(ATTRIBUTE, VALUE, DICTIONARY) \
{ \
id valueToAdd = VALUE; \
if (valueToAdd != nil) { \
CFDictionarySetValue((CFMutableDictionaryRef) DICTIONARY, (CFStringRef) (const void *) ATTRIBUTE, (const void *) valueToAdd); \
} \
}
#endif
#endif

#ifndef MTR_OPTIONAL_COLLECTION_ATTRIBUTE
#define MTR_OPTIONAL_COLLECTION_ATTRIBUTE(ATTRIBUTE, COLLECTION, DICTIONARY) \
if ([COLLECTION count] > 0) { \
CFDictionarySetValue((CFMutableDictionaryRef) DICTIONARY, (CFStringRef) ATTRIBUTE, (const void *) COLLECTION); \
}
#endif

#ifndef MTR_OPTIONAL_NUMBER_ATTRIBUTE
#define MTR_OPTIONAL_NUMBER_ATTRIBUTE(ATTRIBUTE, NUMBER, DICTIONARY) \
if ([NUMBER intValue] != 0) { \
CFDictionarySetValue((CFMutableDictionaryRef) DICTIONARY, (CFStringRef) ATTRIBUTE, (const void *) NUMBER); \
}
#endif

#ifndef MTR_REMOVE_ATTRIBUTE
#define MTR_REMOVE_ATTRIBUTE(ATTRIBUTE, DICTIONARY) \
if (ATTRIBUTE != nil && DICTIONARY) { \
CFDictionaryRemoveValue((CFMutableDictionaryRef) DICTIONARY, (CFStringRef) ATTRIBUTE); \
}
#endif

#ifndef MTR_REQUIRED_ATTRIBUTE
#define MTR_REQUIRED_ATTRIBUTE(ATTRIBUTE, VALUE, DICTIONARY) \
{ \
id valueToAdd = VALUE; \
if (valueToAdd != nil) { \
CFDictionarySetValue((CFMutableDictionaryRef) DICTIONARY, (CFStringRef) ATTRIBUTE, (const void *) valueToAdd); \
} else { \
MTR_LOG_ERROR("Warning, missing %@ to add to %s", ATTRIBUTE, #DICTIONARY); \
} \
}
#endif
8 changes: 7 additions & 1 deletion src/darwin/Framework/CHIP/MTRDeviceController+XPC.mm
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@
static NSSet * GetXPCAllowedClasses()
{
static NSSet * const sXPCAllowedClasses = [NSSet setWithArray:@[
[NSString class], [NSNumber class], [NSData class], [NSArray class], [NSDictionary class], [NSError class]
[NSString class],
[NSNumber class],
[NSData class],
[NSArray class],
[NSDictionary class],
[NSError class],
[NSDate class],
]];
return sXPCAllowedClasses;
}
Expand Down
2 changes: 1 addition & 1 deletion src/darwin/Framework/CHIP/MTRDeviceController_XPC.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ MTR_TESTABLE
- (id)initWithUniqueIdentifier:(NSUUID *)UUID machServiceName:(NSString *)machServiceName options:(NSXPCConnectionOptions)options
#endif

@property(atomic, retain, readwrite)NSXPCConnection * xpcConnection;
@property(nullable, atomic, retain, readwrite)NSXPCConnection * xpcConnection;

@end

Expand Down
175 changes: 143 additions & 32 deletions src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#import "MTRDefines_Internal.h"
#import "MTRDeviceController_Internal.h"
#import "MTRDevice_XPC.h"
#import "MTRDevice_XPC_Internal.h"
#import "MTRLogging_Internal.h"
#import "MTRXPCClientProtocol.h"
#import "MTRXPCServerProtocol.h"
Expand All @@ -33,7 +34,10 @@

@interface MTRDeviceController_XPC ()

@property (nonatomic, retain, readwrite) NSUUID * uniqueIdentifier;
@property (nonatomic, readwrite, retain) NSUUID * uniqueIdentifier;
@property (nonnull, atomic, readwrite, retain) MTRXPCDeviceControllerParameters * xpcParameters;
@property (atomic, readwrite, assign) NSTimeInterval xpcRetryTimeInterval;
@property (atomic, readwrite, assign) BOOL xpcConnectedOrConnecting;

@end

Expand All @@ -48,7 +52,15 @@ - (NSXPCInterface *)_interfaceForServerProtocol
NSXPCInterface * interface = [NSXPCInterface interfaceWithProtocol:@protocol(MTRXPCServerProtocol)];

NSSet * allowedClasses = [NSSet setWithArray:@[
[NSString class], [NSNumber class], [NSData class], [NSArray class], [NSDictionary class], [NSError class], [MTRCommandPath class], [MTRAttributePath class]
[NSString class],
[NSNumber class],
[NSData class],
[NSArray class],
[NSDictionary class],
[NSError class],
[MTRCommandPath class],
[MTRAttributePath class],
[NSDate class],
]];

[interface setClasses:allowedClasses
Expand All @@ -62,7 +74,14 @@ - (NSXPCInterface *)_interfaceForClientProtocol
{
NSXPCInterface * interface = [NSXPCInterface interfaceWithProtocol:@protocol(MTRXPCClientProtocol)];
NSSet * allowedClasses = [NSSet setWithArray:@[
[NSString class], [NSNumber class], [NSData class], [NSArray class], [NSDictionary class], [NSError class], [MTRAttributePath class]
[NSString class],
[NSNumber class],
[NSData class],
[NSArray class],
[NSDictionary class],
[NSError class],
[MTRAttributePath class],
[NSDate class],
]];
[interface setClasses:allowedClasses
forSelector:@selector(device:receivedAttributeReport:)
Expand All @@ -83,6 +102,114 @@ - (NSXPCInterface *)_interfaceForClientProtocol
return [self.uniqueIdentifier UUIDString];
}

- (void)_startXPCConnectionRetry
{
if (!self.xpcConnectedOrConnecting) {
MTR_LOG("%@: XPC Connection retry - Starting retry for XPC Connection", self);
self.xpcRetryTimeInterval = 0.5;
mtr_weakify(self);

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (self.xpcRetryTimeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
mtr_strongify(self);
[self _xpcConnectionRetry];
});
} else {
MTR_LOG("%@: XPC Connection retry - Not starting retry for XPC Connection, already trying", self);
}
}

- (void)_xpcConnectionRetry
{
MTR_LOG("%@: XPC Connection retry - timer hit", self);
if (!self.xpcConnectedOrConnecting) {
if (![self _setupXPCConnection]) {
#if 0 // FIXME: Not sure why this retry is not working, but I will fix this later
MTR_LOG("%@: XPC Connection retry - Scheduling another retry", self);
self.xpcRetryTimeInterval = self.xpcRetryTimeInterval >= 1 ? self.xpcRetryTimeInterval * 2 : 1;
self.xpcRetryTimeInterval = MIN(60.0, self.xpcRetryTimeInterval);
mtr_weakify(self);

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.xpcRetryTimeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
mtr_strongify(self);
[self _xpcConnectionRetry];
});
#else
MTR_LOG("%@: XPC Connection failed retry - bailing", self);
#endif
} else {
MTR_LOG("%@: XPC Connection retry - connection attempt successful", self);
}
} else {
MTR_LOG("%@: XPC Connection retry - Mid retry, or connected, stopping retry timer", self);
}
}

- (BOOL)_setupXPCConnection
{
self.xpcConnection = self.xpcParameters.xpcConnectionBlock();

MTR_LOG("%@ Set up XPC Connection: %@", self, self.xpcConnection);
if (self.xpcConnection) {
mtr_weakify(self);
self.xpcConnection.remoteObjectInterface = [self _interfaceForServerProtocol];

self.xpcConnection.exportedInterface = [self _interfaceForClientProtocol];
self.xpcConnection.exportedObject = self;

self.xpcConnection.interruptionHandler = ^{
mtr_strongify(self);
MTR_LOG_ERROR("XPC Connection for device controller interrupted: %@", self.xpcParameters.uniqueIdentifier);
self.xpcConnectedOrConnecting = NO;
self.xpcConnection = nil;
[self _startXPCConnectionRetry];
};

self.xpcConnection.invalidationHandler = ^{
mtr_strongify(self);
MTR_LOG_ERROR("XPC Connection for device controller invalidated: %@", self.xpcParameters.uniqueIdentifier);
self.xpcConnectedOrConnecting = NO;
self.xpcConnection = nil;
[self _startXPCConnectionRetry];
};

MTR_LOG("%@ Activating new XPC connection", self);
[self.xpcConnection activate];

[[self.xpcConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
MTR_LOG_ERROR("Checkin error: %@", error);
}] deviceController:self.uniqueIdentifier checkInWithContext:[NSDictionary dictionary]];

// FIXME: Trying to kick all the MTRDevices attached to this controller to re-establish connections
// This state needs to be stored properly and re-established at connnection time

MTR_LOG("%@ Starting existing NodeID Registration", self);
for (NSNumber * nodeID in [self.nodeIDToDeviceMap keyEnumerator]) {
MTR_LOG("%@ => Registering nodeID: %@", self, nodeID);
mtr_weakify(self);

[[self.xpcConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
mtr_strongify(self);
MTR_LOG_ERROR("%@ Registration error for device nodeID: %@ : %@", self, nodeID, error);
}] deviceController:self.uniqueIdentifier registerNodeID:nodeID];
}

__block BOOL barrierComplete = NO;

[self.xpcConnection scheduleSendBarrierBlock:^{
barrierComplete = YES;
MTR_LOG("%@: Barrier complete: %d", self, barrierComplete);
}];

MTR_LOG("%@ Done existing NodeID Registration, barrierComplete: %d", self, barrierComplete);
self.xpcConnectedOrConnecting = barrierComplete;
} else {
MTR_LOG_ERROR("%@ Failed to set up XPC Connection", self);
self.xpcConnectedOrConnecting = NO;
}

return (self.xpcConnectedOrConnecting);
}

- (nullable instancetype)initWithParameters:(MTRDeviceControllerAbstractParameters *)parameters
error:(NSError * __autoreleasing *)error
{
Expand Down Expand Up @@ -110,30 +237,12 @@ - (nullable instancetype)initWithParameters:(MTRDeviceControllerAbstractParamete
return nil;
}

self.xpcConnection = connectionBlock();
self.uniqueIdentifier = UUID;
self.xpcParameters = xpcParameters;
self.chipWorkQueue = dispatch_queue_create("MTRDeviceController_XPC_queue", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
self.nodeIDToDeviceMap = [NSMapTable strongToWeakObjectsMapTable];
self.uniqueIdentifier = UUID;

MTR_LOG("Set up XPC Connection: %@", self.xpcConnection);
if (self.xpcConnection) {
self.xpcConnection.remoteObjectInterface = [self _interfaceForServerProtocol];

self.xpcConnection.exportedInterface = [self _interfaceForClientProtocol];
self.xpcConnection.exportedObject = self;

self.xpcConnection.interruptionHandler = ^{
MTR_LOG_ERROR("XPC Connection for device controller interrupted: %@", UUID);
};

self.xpcConnection.invalidationHandler = ^{
MTR_LOG_ERROR("XPC Connection for device controller invalidated: %@", UUID);
};

MTR_LOG("Activating new XPC connection");
[self.xpcConnection activate];
} else {
MTR_LOG_ERROR("Failed to set up XPC Connection");
if (![self _setupXPCConnection]) {
return nil;
}
}
Expand All @@ -159,7 +268,7 @@ - (id)initWithUniqueIdentifier:(NSUUID *)UUID machServiceName:(NSString *)machSe
self.xpcConnection.exportedObject = self;

MTR_LOG("%s: resuming new XPC connection");
[self.xpcConnection resume];
[self.xpcConnection activate];
} else {
MTR_LOG_ERROR("Failed to set up XPC Connection");
return nil;
Expand All @@ -177,13 +286,7 @@ - (MTRDevice *)_setupDeviceForNodeID:(NSNumber *)nodeID prefetchedClusterData:(N
os_unfair_lock_assert_owner(self.deviceMapLock);

MTRDevice * deviceToReturn = [[MTRDevice_XPC alloc] initWithNodeID:nodeID controller:self];
// If we're not running, don't add the device to our map. That would
// create a cycle that nothing would break. Just return the device,
// which will be in exactly the state it would be in if it were created
// while we were running and then we got shut down.
if ([self isRunning]) {
[self.nodeIDToDeviceMap setObject:deviceToReturn forKey:nodeID];
}
[self.nodeIDToDeviceMap setObject:deviceToReturn forKey:nodeID];
MTR_LOG("%s: returning XPC device for node id %@", __PRETTY_FUNCTION__, nodeID);
return deviceToReturn;
}
Expand Down Expand Up @@ -255,6 +358,14 @@ - (oneway void)deviceConfigurationChanged:(NSNumber *)nodeID
[device deviceConfigurationChanged:nodeID];
}

- (oneway void)device:(NSNumber *)nodeID internalStateUpdated:(NSDictionary *)dictionary
{
MTRDevice_XPC * device = (MTRDevice_XPC *) [self deviceForNodeID:nodeID];
MTR_LOG("Received internalStateUpdated: %@ found device: %@", nodeID, device);

[device device:nodeID internalStateUpdated:dictionary];
}

#pragma mark - MTRDeviceController Protocol Client

// Not Supported via XPC
Expand Down
Loading
Loading