diff --git a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj index cc9f004573bff8..145e043b15828c 100644 --- a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ 51B22C262740CB32008D5055 /* CHIPStructsObjc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51B22C252740CB32008D5055 /* CHIPStructsObjc.mm */; }; 51B22C2A2740CB47008D5055 /* CHIPCommandPayloadsObjc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51B22C292740CB47008D5055 /* CHIPCommandPayloadsObjc.mm */; }; 51E24E73274E0DAC007CCF6E /* CHIPErrorTestUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51E24E72274E0DAC007CCF6E /* CHIPErrorTestUtils.mm */; }; + 5A60370827EA1FF60020DB79 /* CHIPAttributeCacheContainer+XPC.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A60370727EA1FF60020DB79 /* CHIPAttributeCacheContainer+XPC.h */; }; 5A6FEC9027B563D900F25F42 /* CHIPDeviceControllerOverXPC.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A6FEC8F27B563D900F25F42 /* CHIPDeviceControllerOverXPC.m */; }; 5A6FEC9227B5669C00F25F42 /* CHIPDeviceControllerOverXPC.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A6FEC8D27B5624E00F25F42 /* CHIPDeviceControllerOverXPC.h */; }; 5A6FEC9627B5983000F25F42 /* CHIPDeviceControllerXPCConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A6FEC9527B5983000F25F42 /* CHIPDeviceControllerXPCConnection.m */; }; @@ -52,9 +53,7 @@ 5A7947DE27BEC3F500434CF2 /* CHIPXPCListenerSampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A7947DD27BEC3F500434CF2 /* CHIPXPCListenerSampleTests.m */; }; 5A7947E427C0129600434CF2 /* CHIPDeviceController+XPC.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A7947E327C0129500434CF2 /* CHIPDeviceController+XPC.m */; }; 5A7947E527C0129F00434CF2 /* CHIPDeviceController+XPC.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A7947E227C0101200434CF2 /* CHIPDeviceController+XPC.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5A830D6A27CFCB640053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A830D6927CFCB570053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.h */; }; 5A830D6C27CFCF590053B85D /* CHIPDeviceControllerOverXPC_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A830D6B27CFCF590053B85D /* CHIPDeviceControllerOverXPC_Internal.h */; }; - 5A830D6E27CFCFF90053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A830D6D27CFCFF90053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.m */; }; 5ACDDD7A27CD129700EFD68A /* CHIPAttributeCacheContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5ACDDD7927CD129700EFD68A /* CHIPAttributeCacheContainer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5ACDDD7D27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5ACDDD7C27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm */; }; 5ACDDD7E27CD3F3A00EFD68A /* CHIPAttributeCacheContainer_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5ACDDD7B27CD14AF00EFD68A /* CHIPAttributeCacheContainer_Internal.h */; }; @@ -134,6 +133,7 @@ 51B22C252740CB32008D5055 /* CHIPStructsObjc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CHIPStructsObjc.mm; path = "zap-generated/CHIPStructsObjc.mm"; sourceTree = ""; }; 51B22C292740CB47008D5055 /* CHIPCommandPayloadsObjc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CHIPCommandPayloadsObjc.mm; path = "zap-generated/CHIPCommandPayloadsObjc.mm"; sourceTree = ""; }; 51E24E72274E0DAC007CCF6E /* CHIPErrorTestUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPErrorTestUtils.mm; sourceTree = ""; }; + 5A60370727EA1FF60020DB79 /* CHIPAttributeCacheContainer+XPC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CHIPAttributeCacheContainer+XPC.h"; sourceTree = ""; }; 5A6FEC8B27B5609C00F25F42 /* CHIPDeviceOverXPC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPDeviceOverXPC.h; sourceTree = ""; }; 5A6FEC8D27B5624E00F25F42 /* CHIPDeviceControllerOverXPC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPDeviceControllerOverXPC.h; sourceTree = ""; }; 5A6FEC8F27B563D900F25F42 /* CHIPDeviceControllerOverXPC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CHIPDeviceControllerOverXPC.m; sourceTree = ""; }; @@ -144,9 +144,7 @@ 5A7947DD27BEC3F500434CF2 /* CHIPXPCListenerSampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CHIPXPCListenerSampleTests.m; sourceTree = ""; }; 5A7947E227C0101200434CF2 /* CHIPDeviceController+XPC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CHIPDeviceController+XPC.h"; sourceTree = ""; }; 5A7947E327C0129500434CF2 /* CHIPDeviceController+XPC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CHIPDeviceController+XPC.m"; sourceTree = ""; }; - 5A830D6927CFCB570053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CHIPDeviceControllerOverXPC+AttributeCache.h"; sourceTree = ""; }; 5A830D6B27CFCF590053B85D /* CHIPDeviceControllerOverXPC_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPDeviceControllerOverXPC_Internal.h; sourceTree = ""; }; - 5A830D6D27CFCFF90053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CHIPDeviceControllerOverXPC+AttributeCache.m"; sourceTree = ""; }; 5ACDDD7927CD129700EFD68A /* CHIPAttributeCacheContainer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPAttributeCacheContainer.h; sourceTree = ""; }; 5ACDDD7B27CD14AF00EFD68A /* CHIPAttributeCacheContainer_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPAttributeCacheContainer_Internal.h; sourceTree = ""; }; 5ACDDD7C27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPAttributeCacheContainer.mm; sourceTree = ""; }; @@ -272,6 +270,7 @@ 2C222ACF255C620600E446B9 /* CHIPDevice.mm */, 2C8C8FBE253E0C2100797F05 /* CHIPPersistentStorageDelegate.h */, 5ACDDD7927CD129700EFD68A /* CHIPAttributeCacheContainer.h */, + 5A60370727EA1FF60020DB79 /* CHIPAttributeCacheContainer+XPC.h */, 5ACDDD7B27CD14AF00EFD68A /* CHIPAttributeCacheContainer_Internal.h */, 5ACDDD7C27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm */, 997DED152695343400975E97 /* CHIPThreadOperationalDataset.mm */, @@ -309,8 +308,6 @@ 5A6FEC8D27B5624E00F25F42 /* CHIPDeviceControllerOverXPC.h */, 5A830D6B27CFCF590053B85D /* CHIPDeviceControllerOverXPC_Internal.h */, 5A6FEC8F27B563D900F25F42 /* CHIPDeviceControllerOverXPC.m */, - 5A830D6927CFCB570053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.h */, - 5A830D6D27CFCFF90053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.m */, 5A6FEC9427B5976200F25F42 /* CHIPDeviceControllerXPCConnection.h */, 5A6FEC9527B5983000F25F42 /* CHIPDeviceControllerXPCConnection.m */, ); @@ -369,6 +366,7 @@ 997DED182695344800975E97 /* CHIPThreadOperationalDataset.h in Headers */, 9956064426420367000C28DE /* CHIPSetupPayload_Internal.h in Headers */, 5A830D6C27CFCF590053B85D /* CHIPDeviceControllerOverXPC_Internal.h in Headers */, + 5A60370827EA1FF60020DB79 /* CHIPAttributeCacheContainer+XPC.h in Headers */, 5ACDDD7E27CD3F3A00EFD68A /* CHIPAttributeCacheContainer_Internal.h in Headers */, 998F286D26D55E10001846C6 /* CHIPKeypair.h in Headers */, 1ED276E426C5832500547A89 /* CHIPCluster.h in Headers */, @@ -379,7 +377,6 @@ 1EC4CE6425CC276600D7304F /* CHIPClustersObjc.h in Headers */, 2C5EEEF6268A85C400CAE3D3 /* CHIPDeviceConnectionBridge.h in Headers */, 2C8C8FC0253E0C2100797F05 /* CHIPPersistentStorageDelegateBridge.h in Headers */, - 5A830D6A27CFCB640053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.h in Headers */, 998F286F26D55EC5001846C6 /* CHIPP256KeypairBridge.h in Headers */, 2C222ADF255C811800E446B9 /* CHIPDevice_Internal.h in Headers */, 991DC08B247704DC00C13860 /* CHIPLogging.h in Headers */, @@ -538,7 +535,6 @@ B2E0D7B6245B0B5C003C5B48 /* CHIPManualSetupPayloadParser.mm in Sources */, 5A6FEC9827B5C6AF00F25F42 /* CHIPDeviceOverXPC.m in Sources */, 51431AF927D2973E008A7943 /* CHIPIMDispatch.mm in Sources */, - 5A830D6E27CFCFF90053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.m in Sources */, 51431AFB27D29CA4008A7943 /* ota-provider.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -612,7 +608,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", - "CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=", + "CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -647,7 +643,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_PREPROCESSOR_DEFINITIONS = ( CHIP_HAVE_CONFIG_H, - "CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=", + "CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=", "$(inherited)", ); HEADER_SEARCH_PATHS = ( @@ -790,7 +786,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_PREPROCESSOR_DEFINITIONS = ( CHIP_HAVE_CONFIG_H, - "CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=", + "CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=", "$(inherited)", ); HEADER_SEARCH_PATHS = ( diff --git a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.h b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer+XPC.h similarity index 50% rename from src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.h rename to src/darwin/Framework/CHIP/CHIPAttributeCacheContainer+XPC.h index 407bb9d4404dc3..fb9cc654d7eb9a 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.h +++ b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer+XPC.h @@ -17,24 +17,14 @@ #import -#import "CHIPDeviceControllerOverXPC.h" +#import "CHIPAttributeCacheContainer.h" NS_ASSUME_NONNULL_BEGIN -@class CHIPSubscribeParams; - -@interface CHIPDeviceControllerOverXPC (AttributeCache) - -- (void)subscribeAttributeCacheWithNodeId:(uint64_t)nodeId - params:(CHIPSubscribeParams * _Nullable)params - completion:(void (^)(NSError * _Nullable error))completion; - -- (void)readAttributeCacheWithNodeId:(uint64_t)nodeId - endpointId:(NSNumber * _Nullable)endpointId - clusterId:(NSNumber * _Nullable)clusterId - attributeId:(NSNumber * _Nullable)attributeId - completion:(void (^)(id _Nullable values, NSError * _Nullable error))completion; - +@interface CHIPAttributeCacheContainer (XPC) +- (void)setXPCConnection:(CHIPDeviceControllerXPCConnection *)xpcConnection + controllerId:(id)controllerId + deviceId:(uint64_t)deviceId; @end NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.h b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.h index 2c72c24cf65f2b..d695cb23977271 100644 --- a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.h +++ b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.h @@ -25,21 +25,6 @@ NS_ASSUME_NONNULL_BEGIN @interface CHIPAttributeCacheContainer : NSObject -/** - * Subscribes to all attributes to update attribute cache. - * - * @param deviceController device controller to retrieve connected device from - * @param deviceId device identifier of the device to cache attributes of - * @param params subscription parameters - * @param clientQueue client queue to dispatch the completion handler through - * @param completion completion handler - */ -- (void)subscribeWithDeviceController:(CHIPDeviceController *)deviceController - deviceId:(uint64_t)deviceId - params:(CHIPSubscribeParams * _Nullable)params - clientQueue:(dispatch_queue_t)clientQueue - completion:(void (^)(NSError * _Nullable error))completion; - /** * Reads an attribute with specific attribute path * diff --git a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm index 59c7ec701ff051..8a06c078dd985a 100644 --- a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm +++ b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm @@ -19,7 +19,7 @@ #import "CHIPAttributeCacheContainer_Internal.h" #import "CHIPCluster.h" -#import "CHIPDeviceControllerOverXPC+AttributeCache.h" +#import "CHIPDeviceControllerXPCConnection.h" #import "CHIPDevice_Internal.h" #import "CHIPError.h" #import "CHIPError_Internal.h" @@ -31,26 +31,6 @@ using namespace chip; -void ContainerAttributeCacheCallback::OnDone() -{ - dispatch_async(DeviceLayer::PlatformMgrImpl().GetWorkQueue(), ^{ - CHIPAttributeCacheContainer * container = attributeCacheContainer; - if (container) { - CHIP_LOG_ERROR("Attribute cache read client done for device %llu", container.deviceId); - if (container.cppReadClient) { - delete container.cppReadClient; - container.cppReadClient = nullptr; - } - if (container.cppAttributeCache) { - delete container.cppAttributeCache; - container.cppAttributeCache = nullptr; - } - } else { - CHIP_LOG_ERROR("Attribute cache read client done for a released cache container"); - } - }); -} - @implementation CHIPAttributeCacheContainer - (instancetype)init @@ -58,10 +38,7 @@ - (instancetype)init if ([super init]) { _cppAttributeCache = nullptr; _cppReadClient = nullptr; - _attributeCacheCallback = new ContainerAttributeCacheCallback; - if (_attributeCacheCallback) { - _attributeCacheCallback->SetContainer(self); - } + _shouldUseXPC = NO; } return self; } @@ -74,80 +51,35 @@ - (void)dealloc if (_cppAttributeCache) { delete _cppAttributeCache; } - if (_attributeCacheCallback) { - delete _attributeCacheCallback; - } } -- (void)subscribeWithDeviceController:(CHIPDeviceController *)deviceController - deviceId:(uint64_t)deviceId - params:(CHIPSubscribeParams * _Nullable)params - clientQueue:clientQueue - completion:(void (^)(NSError * _Nullable error))completion +- (void)setXPCConnection:(CHIPDeviceControllerXPCConnection *)xpcConnection + controllerId:(id)controllerId + deviceId:(uint64_t)deviceId { - __auto_type workQueue = DeviceLayer::PlatformMgrImpl().GetWorkQueue(); - __auto_type completionHandler = ^(NSError * _Nullable error) { - dispatch_async(clientQueue, ^{ - completion(error); - }); - }; - if ([deviceController isKindOfClass:[CHIPDeviceControllerOverXPC class]]) { - self.deviceId = deviceId; - CHIPDeviceControllerOverXPC * xpcDeviceController = (CHIPDeviceControllerOverXPC *) deviceController; - self.xpcDeviceController = xpcDeviceController; - [xpcDeviceController subscribeAttributeCacheWithNodeId:deviceId params:params completion:completionHandler]; - return; - } - [deviceController - getConnectedDevice:deviceId - queue:workQueue - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - CHIP_LOG_ERROR("Error: Failed to get connected device (%llu) for attribute cache: %@", deviceId, error); - completionHandler(error); - return; - } - if (self.cppReadClient) { - delete self.cppReadClient; - self.cppReadClient = nullptr; - } - if (self.cppAttributeCache) { - delete self.cppAttributeCache; - } - self.cppAttributeCache = new app::AttributeCache(*self.attributeCacheCallback); - if (!self.cppAttributeCache) { - CHIP_LOG_ERROR("Error: Failed to allocate attribute cache for device %llu", deviceId); - completionHandler([NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); - return; - } + self.xpcConnection = xpcConnection; + self.xpcControllerId = controllerId; + self.deviceId = deviceId; + self.shouldUseXPC = YES; +} - __auto_type engine = app::InteractionModelEngine::GetInstance(); - __auto_type readClient = new app::ReadClient(engine, [device internalDevice]->GetExchangeManager(), - self.cppAttributeCache->GetBufferedCallback(), app::ReadClient::InteractionType::Subscribe); - if (!readClient) { - CHIP_LOG_ERROR("Error: Failed to allocate attribute cache read client for device %llu", deviceId); - completionHandler([NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); - return; - } - self.deviceId = deviceId; - app::ReadPrepareParams readParams([device internalDevice]->GetSecureSession().Value()); - static app::AttributePathParams attributePath; - readParams.mpAttributePathParamsList = &attributePath; - readParams.mAttributePathParamsListSize = 1; - readParams.mMaxIntervalCeilingSeconds = 43200; - readParams.mIsFabricFiltered = (params == nil || params.fabricFiltered == nil || [params.fabricFiltered boolValue]); - readParams.mKeepSubscriptions - = (params != nil && params.keepPreviousSubscriptions != nil && [params.keepPreviousSubscriptions boolValue]); - __auto_type err = readClient->SendAutoResubscribeRequest(std::move(readParams)); - if (err != CHIP_NO_ERROR) { - CHIP_LOG_ERROR("Error: attribute cache subscription failed for device %llu: %s", deviceId, ErrorStr(err)); - completionHandler([NSError errorWithDomain:CHIPErrorDomain code:err.AsInteger() userInfo:nil]); - return; - } - self.cppReadClient = readClient; - CHIP_LOG_DEBUG("Attribute cache subscription succeeded for device %llu", deviceId); - completionHandler(nil); - }]; +- (void)adoptReadClient:(std::unique_ptr)readClient + attributeCache:(std::unique_ptr)attributeCache +{ + _cppReadClient = readClient.release(); + _cppAttributeCache = attributeCache.release(); +} + +- (void)releaseReadClient +{ + if (_cppReadClient) { + delete _cppReadClient; + _cppReadClient = nullptr; + } + if (_cppAttributeCache) { + delete _cppAttributeCache; + _cppAttributeCache = nullptr; + } } static CHIP_ERROR AppendAttibuteValueToArray( @@ -189,21 +121,36 @@ - (void)readAttributeWithEndpointId:(NSNumber * _Nullable)endpointId }); }; - if (self.xpcDeviceController) { - CHIPDeviceControllerOverXPC * strongController = self.xpcDeviceController; - if (strongController) { - [strongController - readAttributeCacheWithNodeId:self.deviceId - endpointId:endpointId - clusterId:clusterId - attributeId:attributeId - completion:(void (^)(id _Nullable values, NSError * _Nullable error)) completionHandler]; - } else { - CHIP_LOG_ERROR("Reading attribute cache failed when associated device controller is deleted"); - completionHandler(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); + if (self.shouldUseXPC) { + CHIPDeviceControllerXPCConnection * xpcConnection = self.xpcConnection; + if (!xpcConnection) { + CHIP_LOG_ERROR("Attribute cache read failed: CHIPDeviceController was already disposed"); + completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); + return; } + __auto_type controllerId = self.xpcControllerId; + uint64_t nodeId = self.deviceId; + [xpcConnection getProxyHandleWithCompletion:^( + dispatch_queue_t _Nonnull queue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) { + if (handle) { + [handle.proxy readAttributeCacheWithController:controllerId + nodeId:nodeId + endpointId:endpointId + clusterId:clusterId + attributeId:attributeId + completion:^(id _Nullable values, NSError * _Nullable error) { + completion([CHIPDeviceController decodeXPCResponseValues:values], error); + __auto_type handleRetainer = handle; + (void) handleRetainer; + }]; + } else { + CHIP_LOG_ERROR("Attribute cache read failed due to XPC connection failure"); + completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); + } + }]; return; } + dispatch_async(DeviceLayer::PlatformMgrImpl().GetWorkQueue(), ^{ if (endpointId == nil && clusterId == nil) { CHIP_LOG_ERROR("Error: currently read from attribute cache does not support wildcards for both endpoint and cluster"); diff --git a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer_Internal.h b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer_Internal.h index ed9370bcfe952b..a17ef5d3dd3237 100644 --- a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer_Internal.h +++ b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer_Internal.h @@ -24,24 +24,18 @@ NS_ASSUME_NONNULL_BEGIN -class ContainerAttributeCacheCallback : public chip::app::AttributeCache::Callback { -public: - void SetContainer(CHIPAttributeCacheContainer * container) { attributeCacheContainer = container; } - - void OnDone() override; - -private: - __weak CHIPAttributeCacheContainer * _Nullable attributeCacheContainer; -}; - @interface CHIPAttributeCacheContainer () -@property (nonatomic, readwrite) chip::app::AttributeCache * _Nullable cppAttributeCache; -@property (nonatomic, readwrite) chip::app::ReadClient * _Nullable cppReadClient; -@property (nonatomic, readwrite) ContainerAttributeCacheCallback * _Nullable attributeCacheCallback; +@property (nonatomic, readwrite) chip::app::AttributeCache * cppAttributeCache; +@property (nonatomic, readwrite) chip::app::ReadClient * cppReadClient; @property (nonatomic, readwrite) uint64_t deviceId; -@property (atomic, readwrite, weak) CHIPDeviceControllerOverXPC * _Nullable xpcDeviceController; +@property (nonatomic, readwrite, weak, nullable) CHIPDeviceControllerXPCConnection * xpcConnection; +@property (nonatomic, readwrite, strong, nullable) id xpcControllerId; +@property (atomic, readwrite) BOOL shouldUseXPC; +- (void)adoptReadClient:(std::unique_ptr)readClient + attributeCache:(std::unique_ptr)attributeCache; +- (void)releaseReadClient; @end NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/CHIPDevice.h b/src/darwin/Framework/CHIP/CHIPDevice.h index eac211d1593ba8..f321beb9287246 100644 --- a/src/darwin/Framework/CHIP/CHIPDevice.h +++ b/src/darwin/Framework/CHIP/CHIPDevice.h @@ -89,6 +89,7 @@ extern NSString * const kCHIPNullValueType; extern NSString * const kCHIPStructureValueType; extern NSString * const kCHIPArrayValueType; +@class CHIPAttributeCacheContainer; @class CHIPReadParams; @class CHIPSubscribeParams; @@ -101,6 +102,9 @@ extern NSString * const kCHIPArrayValueType; * Subscribe to receive attribute reports for everything (all endpoints, all * clusters, all attributes, all events) on the device. * + * A non-nil attribute cache container will cache attribute values, retrievable + * through the designated attribute cache container. + * * reportHandler will be called any time a data update is available (with a * non-nil "value" and nil "error"), or any time there is an error for the * entire subscription (with a nil "value" and non-nil "error"). If it's called @@ -110,6 +114,8 @@ extern NSString * const kCHIPArrayValueType; * instances. Errors for specific paths, not the whole subscription, will be * reported via those objects. * + * reportHandler is not supported over XPC at the moment. + * * subscriptionEstablished block, if not nil, will be called once the * subscription is established. This will be _after_ the first (priming) call * to reportHandler. Note that if the CHIPSubscribeParams are set to @@ -121,6 +127,7 @@ extern NSString * const kCHIPArrayValueType; minInterval:(uint16_t)minInterval maxInterval:(uint16_t)maxInterval params:(nullable CHIPSubscribeParams *)params + cacheContainer:(CHIPAttributeCacheContainer * _Nullable)attributeCacheContainer reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler subscriptionEstablished:(nullable void (^)(void))subscriptionEstablishedHandler; diff --git a/src/darwin/Framework/CHIP/CHIPDevice.mm b/src/darwin/Framework/CHIP/CHIPDevice.mm index 9528022aadea4f..4827ca71c235ff 100644 --- a/src/darwin/Framework/CHIP/CHIPDevice.mm +++ b/src/darwin/Framework/CHIP/CHIPDevice.mm @@ -15,6 +15,7 @@ * limitations under the License. */ +#import "CHIPAttributeCacheContainer_Internal.h" #import "CHIPAttributeTLVValueDecoder_Internal.h" #import "CHIPCallbackBridgeBase_internal.h" #import "CHIPCluster.h" @@ -26,6 +27,7 @@ #include "lib/core/CHIPError.h" #include "lib/core/DataModelTypes.h" +#include #include #include #include @@ -236,7 +238,7 @@ - (instancetype)initWithDevice:(chip::DeviceProxy *)device namespace { -class SubscriptionCallback final : public ReadClient::Callback { +class SubscriptionCallback final : public AttributeCache::Callback { public: SubscriptionCallback(dispatch_queue_t queue, ReportCallback reportCallback, SubscriptionEstablishedHandler _Nullable subscriptionEstablishedHandler) @@ -247,6 +249,16 @@ - (instancetype)initWithDevice:(chip::DeviceProxy *)device { } + SubscriptionCallback(dispatch_queue_t queue, ReportCallback reportCallback, + SubscriptionEstablishedHandler _Nullable subscriptionEstablishedHandler, void (^onDoneHandler)(void)) + : mQueue(queue) + , mReportCallback(reportCallback) + , mSubscriptionEstablishedHandler(subscriptionEstablishedHandler) + , mBufferedReadAdapter(*this) + , mOnDoneHandler(onDoneHandler) + { + } + BufferedReadCallback & GetBufferedCallback() { return mBufferedReadAdapter; } // We need to exist to get a ReadClient, so can't take this as a constructor argument. @@ -294,6 +306,7 @@ - (instancetype)initWithDevice:(chip::DeviceProxy *)device // deletion. std::unique_ptr mReadClient; bool mHaveQueuedDeletion = false; + void (^mOnDoneHandler)(void) = nil; }; } // anonymous namespace @@ -302,6 +315,7 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue minInterval:(uint16_t)minInterval maxInterval:(uint16_t)maxInterval params:(nullable CHIPSubscribeParams *)params + cacheContainer:(CHIPAttributeCacheContainer * _Nullable)attributeCacheContainer reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler subscriptionEstablished:(nullable void (^)(void))subscriptionEstablishedHandler { @@ -323,9 +337,25 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue readParams.mKeepSubscriptions = (params != nil) && (params.keepPreviousSubscriptions != nil) && [params.keepPreviousSubscriptions boolValue]; - auto callback = std::make_unique(queue, reportHandler, subscriptionEstablishedHandler); - auto readClient = std::make_unique(InteractionModelEngine::GetInstance(), device->GetExchangeManager(), - callback->GetBufferedCallback(), ReadClient::InteractionType::Subscribe); + std::unique_ptr callback; + std::unique_ptr readClient; + std::unique_ptr attributeCache; + if (attributeCacheContainer) { + __weak CHIPAttributeCacheContainer * weakPtr = attributeCacheContainer; + callback = std::make_unique(queue, reportHandler, subscriptionEstablishedHandler, ^{ + CHIPAttributeCacheContainer * container = weakPtr; + if (container) { + [container releaseReadClient]; + } + }); + attributeCache = std::make_unique(*callback.get()); + readClient = std::make_unique(InteractionModelEngine::GetInstance(), device->GetExchangeManager(), + attributeCache->GetBufferedCallback(), ReadClient::InteractionType::Subscribe); + } else { + callback = std::make_unique(queue, reportHandler, subscriptionEstablishedHandler); + readClient = std::make_unique(InteractionModelEngine::GetInstance(), device->GetExchangeManager(), + callback->GetBufferedCallback(), ReadClient::InteractionType::Subscribe); + } CHIP_ERROR err; if (params != nil && params.autoResubscribe != nil && ![params.autoResubscribe boolValue]) { @@ -344,9 +374,14 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue return; } - // Callback and ReadClient will be deleted when OnDone is called or an error is - // encountered. - callback->AdoptReadClient(std::move(readClient)); + if (attributeCacheContainer) { + // When using attribute cache, the attribute cache container manages the readclient object and cache object. + [attributeCacheContainer adoptReadClient:std::move(readClient) attributeCache:std::move(attributeCache)]; + } else { + // Callback and ReadClient will be deleted when OnDone is called or an error is + // encountered. + callback->AdoptReadClient(std::move(readClient)); + } callback.release(); } @@ -1310,6 +1345,9 @@ - (instancetype)initWithPath:(const ConcreteDataAttributePath &)path value:(null void SubscriptionCallback::OnDone() { + if (mOnDoneHandler) { + mOnDoneHandler(); + } if (!mHaveQueuedDeletion) { delete this; return; // Make sure we touch nothing else. diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h b/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h index 8bb366ba99235f..a81eadfcd6099c 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h +++ b/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h @@ -139,12 +139,15 @@ NS_ASSUME_NONNULL_BEGIN - (void)stopReportsWithController:(id _Nullable)controller nodeId:(uint64_t)nodeId completion:(void (^)(void))completion; /** - * Requests a specific node attribute subscription into a cache - */ -- (void)subscribeAttributeCacheWithController:(id _Nullable)controller - nodeId:(uint64_t)nodeId - params:(NSDictionary * _Nullable)params - completion:(void (^)(NSError * _Nullable error))completion; + * Requests subscription of all attributes. + */ +- (void)subscribeWithController:(id _Nullable)controller + nodeId:(uint64_t)nodeId + minInterval:(NSNumber *)minInterval + maxInterval:(NSNumber *)maxInterval + params:(NSDictionary * _Nullable)params + shouldCache:(BOOL)shouldCache + completion:(void (^)(NSError * _Nullable error))completion; /** * Requests reading attribute cache diff --git a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.m b/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.m deleted file mode 100644 index 7ea55db94246b5..00000000000000 --- a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.m +++ /dev/null @@ -1,124 +0,0 @@ -/** - * - * Copyright (c) 2022 Project CHIP Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "CHIPDeviceControllerOverXPC+AttributeCache.h" -#import "CHIPDeviceControllerOverXPC_Internal.h" -#import "CHIPError.h" -#import "CHIPLogging.h" - -NS_ASSUME_NONNULL_BEGIN - -@implementation CHIPDeviceControllerOverXPC (AttributeCache) - -- (void)subscribeAttributeCacheWithNodeId:(uint64_t)nodeId - params:(CHIPSubscribeParams * _Nullable)params - completion:(void (^)(NSError * _Nullable error))completion -{ - dispatch_async(self.workQueue, ^{ - dispatch_group_t group = dispatch_group_create(); - if (!self.controllerId) { - dispatch_group_enter(group); - [self.xpcConnection getProxyHandleWithCompletion:^( - dispatch_queue_t _Nonnull queue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) { - if (handle) { - [handle.proxy getAnyDeviceControllerWithCompletion:^(id _Nullable controller, NSError * _Nullable error) { - if (error) { - CHIP_LOG_ERROR("Failed to fetch any shared remote controller"); - } else { - self.controllerId = controller; - } - dispatch_group_leave(group); - __auto_type handleRetainer = handle; - (void) handleRetainer; - }]; - } else { - CHIP_LOG_ERROR("XPC disconnected while retrieving any shared remote controller"); - dispatch_group_leave(group); - } - }]; - } - dispatch_group_notify(group, self.workQueue, ^{ - if (self.controllerId) { - [self.xpcConnection getProxyHandleWithCompletion:^( - dispatch_queue_t _Nonnull queue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) { - if (handle) { - [handle.proxy subscribeAttributeCacheWithController:self.controllerId - nodeId:nodeId - params:[CHIPDeviceController encodeXPCSubscribeParams:params] - completion:^(NSError * _Nullable error) { - if (error) { - CHIP_LOG_ERROR("Attribute cache subscription for " - "controller %@ and node %llu failed: %@", - self.controllerId, nodeId, error); - completion(error); - return; - } - completion(nil); - __auto_type handleRetainer = handle; - (void) handleRetainer; - }]; - } else { - CHIP_LOG_ERROR( - "Attribute cache subscription for controller %@ and node %llu failed due to XPC connection failure", - self.controllerId, nodeId); - completion([NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); - } - }]; - } else { - CHIP_LOG_ERROR("Attribute cache subscription for node %llu failed due to lack of controller ID", nodeId); - completion([NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); - } - }); - }); -} - -- (void)readAttributeCacheWithNodeId:(uint64_t)nodeId - endpointId:(NSNumber * _Nullable)endpointId - clusterId:(NSNumber * _Nullable)clusterId - attributeId:(NSNumber * _Nullable)attributeId - completion:(void (^)(id _Nullable values, NSError * _Nullable error))completion -{ - dispatch_async(self.workQueue, ^{ - if (!self.controllerId) { - CHIP_LOG_ERROR("Attribute cache wasn't subscribed yet."); - completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); - return; - } - [self.xpcConnection getProxyHandleWithCompletion:^( - dispatch_queue_t _Nonnull queue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) { - if (handle) { - [handle.proxy readAttributeCacheWithController:self.controllerId - nodeId:nodeId - endpointId:endpointId - clusterId:clusterId - attributeId:attributeId - completion:^(id _Nullable values, NSError * _Nullable error) { - completion([CHIPDeviceController decodeXPCResponseValues:values], error); - __auto_type handleRetainer = handle; - (void) handleRetainer; - }]; - } else { - CHIP_LOG_ERROR("Attribute cache read failed due to XPC connection failure"); - completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); - } - }]; - }); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC.m b/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC.m index 3c4ca4d586e704..4ae6f7051693ee 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC.m +++ b/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC.m @@ -15,7 +15,6 @@ * limitations under the License. */ -#import "CHIPDeviceControllerOverXPC+AttributeCache.h" #import "CHIPDeviceControllerOverXPC_Internal.h" #import "CHIPDeviceController+XPC.h" diff --git a/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m b/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m index 8d11e8cb902cee..cd329a11dc8f1a 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m +++ b/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m @@ -17,6 +17,7 @@ #import "CHIPDeviceOverXPC.h" +#import "CHIPAttributeCacheContainer+XPC.h" #import "CHIPCluster.h" #import "CHIPDeviceController+XPC.h" #import "CHIPDeviceControllerXPCConnection.h" @@ -48,14 +49,42 @@ - (instancetype)initWithController:(id)controller - (void)subscribeWithQueue:(dispatch_queue_t)queue minInterval:(uint16_t)minInterval maxInterval:(uint16_t)maxInterval + params:(nullable CHIPSubscribeParams *)params + cacheContainer:(CHIPAttributeCacheContainer * _Nullable)attributeCacheContainer reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler subscriptionEstablished:(void (^_Nullable)(void))subscriptionEstablishedHandler { - dispatch_async(queue, ^{ - CHIP_LOG_ERROR("All attribute subscription is not supported by remote device"); - subscriptionEstablishedHandler(); - reportHandler(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); - }); + CHIP_LOG_DEBUG("Subscribing all attributes... Note that reportHandler is not supported."); + if (attributeCacheContainer) { + [attributeCacheContainer setXPCConnection:_xpcConnection controllerId:self.controller deviceId:self.nodeId]; + } + [_xpcConnection getProxyHandleWithCompletion:^( + dispatch_queue_t _Nonnull proxyQueue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) { + if (handle) { + [handle.proxy subscribeWithController:self.controller + nodeId:self.nodeId + minInterval:@(minInterval) + maxInterval:@(maxInterval) + params:[CHIPDeviceController encodeXPCSubscribeParams:params] + shouldCache:(attributeCacheContainer != nil) + completion:^(NSError * _Nullable error) { + dispatch_async(queue, ^{ + if (error) { + reportHandler(nil, error); + } else { + subscriptionEstablishedHandler(); + } + }); + __auto_type handleRetainer = handle; + (void) handleRetainer; + }]; + } else { + CHIP_LOG_ERROR("Failed to obtain XPC connection to write attribute"); + dispatch_async(queue, ^{ + reportHandler(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); + }); + } + }]; } - (void)readAttributeWithEndpointId:(NSNumber * _Nullable)endpointId diff --git a/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m b/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m index b075c9e4d45838..68884a49946477 100644 --- a/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m +++ b/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m @@ -688,16 +688,25 @@ - (void)test011_ReadCachedAttribute XCTestExpectation * subscribeExpectation = [self expectationWithDescription:@"Subscription complete"]; NSLog(@"Subscribing..."); - [attributeCacheContainer subscribeWithDeviceController:controller - deviceId:kDeviceId - params:nil - clientQueue:queue - completion:^(NSError * _Nullable error) { - NSLog(@"Subscription complete with error: %@", error); - XCTAssertNil(error); - [subscribeExpectation fulfill]; - }]; - [self waitForExpectations:[NSArray arrayWithObject:subscribeExpectation] timeout:kTimeoutInSeconds]; + __block void (^reportHandler)(NSArray * _Nullable value, NSError * _Nullable error); + [device subscribeWithQueue:queue + minInterval:2 + maxInterval:60 + params:nil + cacheContainer:attributeCacheContainer + reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"Received report: %@, error: %@", value, error); + if (reportHandler) { + __auto_type handler = reportHandler; + reportHandler = nil; + handler(value, error); + } + } + subscriptionEstablished:^{ + NSLog(@"Subscription established"); + [subscribeExpectation fulfill]; + }]; + [self waitForExpectations:@[ subscribeExpectation ] timeout:60]; // Invoke command to set the attribute to a known state XCTestExpectation * commandExpectation = [self expectationWithDescription:@"Command invoked"]; @@ -712,11 +721,9 @@ - (void)test011_ReadCachedAttribute }]; [self waitForExpectations:[NSArray arrayWithObject:commandExpectation] timeout:kTimeoutInSeconds]; - // Wait till reports arrive from accessory. It turned out accessory could generate very lengthy initial reports. + // Wait till reports arrive from accessory. NSLog(@"Waiting for reports from accessory..."); - __auto_type idleExpectation = [self expectationWithDescription:@"Must not break out of idle"]; - idleExpectation.inverted = YES; - [self waitForExpectations:[NSArray arrayWithObject:idleExpectation] timeout:60]; + sleep(5); // Read cache NSLog(@"Reading from cache..."); @@ -749,6 +756,19 @@ - (void)test011_ReadCachedAttribute }]; [self waitForExpectations:[NSArray arrayWithObject:newSubscriptionEstablished] timeout:kTimeoutInSeconds]; + __auto_type reportExpectation = [self expectationWithDescription:@"Report handler called"]; + reportHandler = ^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"Report received: %@, error: %@", value, error); + for (CHIPAttributeReport * report in value) { + if ([report.path.endpoint isEqualToNumber:@1] && [report.path.cluster isEqualToNumber:@6] && + [report.path.attribute isEqualToNumber:@0]) { + XCTAssertEqual([report.value boolValue], NO); + [reportExpectation fulfill]; + break; + } + } + }; + NSLog(@"Invoking another command..."); commandExpectation = [self expectationWithDescription:@"Command invoked"]; [cluster offWithCompletionHandler:^(NSError * _Nullable err) { @@ -760,12 +780,10 @@ - (void)test011_ReadCachedAttribute // Wait till reports arrive from accessory. NSLog(@"Waiting for reports from accessory..."); - idleExpectation = [self expectationWithDescription:@"Must not break out of idle"]; - idleExpectation.inverted = YES; - [self waitForExpectations:[NSArray arrayWithObject:idleExpectation] timeout:3]; + [self waitForExpectations:@[ reportExpectation ] timeout:kTimeoutInSeconds]; NSLog(@"Disconnect accessory to test cache..."); - idleExpectation = [self expectationWithDescription:@"Must not break out of idle"]; + __auto_type idleExpectation = [self expectationWithDescription:@"Must not break out of idle"]; idleExpectation.inverted = YES; [self waitForExpectations:[NSArray arrayWithObject:idleExpectation] timeout:10]; diff --git a/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m b/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m index 5e5502d7c35973..90e56447df9df7 100644 --- a/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m +++ b/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m @@ -341,30 +341,54 @@ - (void)stopReportsWithController:(id _Nullable)controller nodeId:(uint64_t)node } } -- (void)subscribeAttributeCacheWithController:(id _Nullable)controller - nodeId:(uint64_t)nodeId - params:(NSDictionary * _Nullable)params - completion:(void (^)(NSError * _Nullable error))completion +- (void)subscribeWithController:(id _Nullable)controller + nodeId:(uint64_t)nodeId + minInterval:(NSNumber *)minInterval + maxInterval:(NSNumber *)maxInterval + params:(NSDictionary * _Nullable)params + shouldCache:(BOOL)shouldCache + completion:(void (^)(NSError * _Nullable error))completion { __auto_type sharedController = [CHIPDeviceController sharedController]; if (sharedController) { - CHIPAttributeCacheContainer * attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init]; - [attributeCacheContainer - subscribeWithDeviceController:sharedController - deviceId:nodeId + CHIPAttributeCacheContainer * attributeCacheContainer; + if (shouldCache) { + attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init]; + } + + [sharedController getConnectedDevice:nodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + if (error) { + NSLog(@"Error: Failed to get connected device (%llu) for attribute cache: %@", nodeId, error); + completion(error); + return; + } + NSMutableArray * established = [NSMutableArray arrayWithCapacity:1]; + [established addObject:@NO]; + [device subscribeWithQueue:dispatch_get_main_queue() + minInterval:[minInterval unsignedShortValue] + maxInterval:[maxInterval unsignedShortValue] params:[CHIPDeviceController decodeXPCSubscribeParams:params] - clientQueue:dispatch_get_main_queue() - completion:^(NSError * _Nullable error) { - NSNumber * nodeIdNumber = [NSNumber numberWithUnsignedLongLong:nodeId]; - if (error) { - NSLog(@"Failed to have subscribe attribute by cache"); - [self.attributeCacheDictionary removeObjectForKey:nodeIdNumber]; - } else { - NSLog(@"Attribute cache for node %llu successfully subscribed attributes", nodeId); - [self.attributeCacheDictionary setObject:attributeCacheContainer forKey:nodeIdNumber]; + cacheContainer:attributeCacheContainer + reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"Report received: %@, error: %@", value, error); + if (error && ![established[0] boolValue]) { + established[0] = @YES; + completion(error); + } } - completion(error); - }]; + subscriptionEstablished:^{ + NSLog(@"Attribute cache subscription succeeded for device %llu", nodeId); + if (attributeCacheContainer) { + [self.attributeCacheDictionary setObject:attributeCacheContainer forKey:@(nodeId)]; + } + if (![established[0] boolValue]) { + established[0] = @YES; + completion(nil); + } + }]; + }]; } else { NSLog(@"Failed to get shared controller"); completion([NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); @@ -418,12 +442,6 @@ - (void)readAttributeCacheWithController:(id _Nullable)controller return mConnectedDevice; } -static CHIPDeviceController * GetDeviceController(void) -{ - XCTAssertNotNil(mDeviceController); - return mDeviceController; -} - @interface CHIPRemoteDeviceSampleTestPairingDelegate : NSObject @property (nonatomic, strong) XCTestExpectation * expectation; @end @@ -1738,23 +1756,22 @@ - (void)test900_SubscribeAttributeCache CHIPDevice * device = GetConnectedDevice(); dispatch_queue_t queue = dispatch_get_main_queue(); - __auto_type * deviceController = GetDeviceController(); CHIPAttributeCacheContainer * attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init]; - NSLog(@"Setting up attribute cache..."); - [attributeCacheContainer subscribeWithDeviceController:deviceController - deviceId:kDeviceId - params:nil - clientQueue:queue - completion:^(NSError * _Nullable error) { - NSLog(@"Attribute cache subscribed attributes"); - [expectation fulfill]; - }]; - [self waitForExpectations:@[ expectation ] timeout:kTimeoutInSeconds]; - - // Wait for initial report to be collected. This can take very long. + NSLog(@"Setting up attribute cache subscription..."); + [device subscribeWithQueue:queue + minInterval:1 + maxInterval:60 + params:nil + cacheContainer:attributeCacheContainer + reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"Report for attribute cache: %@, error: %@", value, error); + } + subscriptionEstablished:^{ + NSLog(@"Attribute cache subscribed attributes"); + [expectation fulfill]; + }]; + // Wait for subscription establishment. This can take very long to collect initial reports. NSLog(@"Waiting for initial report..."); - expectation = [self expectationWithDescription:@"Must not jump out while waiting for initial report"]; - expectation.inverted = YES; [self waitForExpectations:@[ expectation ] timeout:120]; // Send command to reset attribute state diff --git a/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m b/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m index 706b326a63697f..692144636d2bc7 100644 --- a/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m +++ b/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m @@ -76,6 +76,61 @@ - (NSString *)description } @end +@interface CHIPAttributeCacheContainer (Test) +// Obsolete method is moved to this test suite to keep tests compatible +- (void)subscribeWithDeviceController:(CHIPDeviceController *)deviceController + deviceId:(uint64_t)deviceId + params:(CHIPSubscribeParams * _Nullable)params + clientQueue:(dispatch_queue_t)clientQueue + completion:(void (^)(NSError * _Nullable error))completion; +@end + +@implementation CHIPAttributeCacheContainer (Test) +- (void)subscribeWithDeviceController:(CHIPDeviceController *)deviceController + deviceId:(uint64_t)deviceId + params:(CHIPSubscribeParams * _Nullable)params + clientQueue:clientQueue + completion:(void (^)(NSError * _Nullable error))completion +{ + __auto_type workQueue = dispatch_get_main_queue(); + __auto_type completionHandler = ^(NSError * _Nullable error) { + dispatch_async(clientQueue, ^{ + completion(error); + }); + }; + [deviceController getConnectedDevice:deviceId + queue:workQueue + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + if (error) { + NSLog(@"Error: Failed to get connected device (%llu) for attribute cache: %@", deviceId, error); + completionHandler(error); + return; + } + __auto_type established = [NSMutableArray arrayWithCapacity:1]; + [established addObject:@NO]; + [device subscribeWithQueue:clientQueue + minInterval:1 + maxInterval:43200 + params:params + cacheContainer:self + reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) { + NSLog(@"Report received for attribute cache: %@, error: %@", value, error); + if (![established[0] boolValue]) { + established[0] = @YES; + completionHandler(error); + } + } + subscriptionEstablished:^{ + NSLog(@"Attribute cache subscription succeeded for device %llu", deviceId); + if (![established[0] boolValue]) { + established[0] = @YES; + completionHandler(nil); + } + }]; + }]; +} +@end + @interface CHIPXPCProtocolTests : XCTestCase @property (nonatomic, readwrite, strong) NSXPCListener * xpcListener; @@ -103,8 +158,9 @@ @interface CHIPXPCProtocolTests NSNumber * _Nullable clusterId, NSNumber * _Nullable attributeId, NSNumber * minInterval, NSNumber * maxInterval, CHIPSubscribeParams * _Nullable params, void (^establishedHandler)(void)); @property (readwrite, strong) void (^handleStopReports)(id controller, uint64_t nodeId, void (^completion)(void)); -@property (readwrite, strong) void (^handleSubscribeAttributeCache) - (id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error)); +@property (readwrite, strong) void (^handleSubscribeAll)(id controller, uint64_t nodeId, NSNumber * minInterval, + NSNumber * maxInterval, CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error)) + ; @property (readwrite, strong) void (^handleReadAttributeCache) (id controller, uint64_t nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId, NSNumber * _Nullable attributeId, void (^completion)(id _Nullable values, NSError * _Nullable error)); @@ -221,14 +277,18 @@ - (void)stopReportsWithController:(id)controller nodeId:(uint64_t)nodeId complet }); } -- (void)subscribeAttributeCacheWithController:(id _Nullable)controller - nodeId:(uint64_t)nodeId - params:(NSDictionary * _Nullable)params - completion:(void (^)(NSError * _Nullable error))completion +- (void)subscribeWithController:(id _Nullable)controller + nodeId:(uint64_t)nodeId + minInterval:(NSNumber *)minInterval + maxInterval:(NSNumber *)maxInterval + params:(NSDictionary * _Nullable)params + shouldCache:(BOOL)shouldCache + completion:(void (^)(NSError * _Nullable error))completion { dispatch_async(dispatch_get_main_queue(), ^{ - XCTAssertNotNil(self.handleSubscribeAttributeCache); - self.handleSubscribeAttributeCache(controller, nodeId, [CHIPDeviceController decodeXPCSubscribeParams:params], completion); + XCTAssertNotNil(self.handleSubscribeAll); + self.handleSubscribeAll(controller, nodeId, minInterval, maxInterval, + [CHIPDeviceController decodeXPCSubscribeParams:params], shouldCache, completion); }); } @@ -2231,15 +2291,15 @@ - (void)testSubscribeAttributeCacheSuccess __auto_type uuid = self.controllerUUID; __auto_type attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init]; - _handleSubscribeAttributeCache - = ^(id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error)) { - NSLog(@"Subscribe attribute cache called"); - XCTAssertTrue([controller isEqualToString:uuid]); - XCTAssertEqual(nodeId, myNodeId); - XCTAssertNil(params); - [callExpectation fulfill]; - completion(nil); - }; + _handleSubscribeAll = ^(id controller, uint64_t nodeId, NSNumber * minInterval, NSNumber * maxInterval, + CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error)) { + NSLog(@"Subscribe called"); + XCTAssertTrue([controller isEqualToString:uuid]); + XCTAssertEqual(nodeId, myNodeId); + XCTAssertNil(params); + [callExpectation fulfill]; + completion(nil); + }; _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; [attributeCacheContainer subscribeWithDeviceController:_remoteDeviceController @@ -2267,17 +2327,17 @@ - (void)testSubscribeAttributeCacheWithParamsSuccess __auto_type uuid = self.controllerUUID; __auto_type attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init]; - _handleSubscribeAttributeCache - = ^(id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error)) { - NSLog(@"Subscribe attribute cache called"); - XCTAssertTrue([controller isEqualToString:uuid]); - XCTAssertEqual(nodeId, myNodeId); - XCTAssertNotNil(params); - XCTAssertEqual([params.fabricFiltered boolValue], [myParams.fabricFiltered boolValue]); - XCTAssertEqual([params.keepPreviousSubscriptions boolValue], [myParams.keepPreviousSubscriptions boolValue]); - [callExpectation fulfill]; - completion(nil); - }; + _handleSubscribeAll = ^(id controller, uint64_t nodeId, NSNumber * minInterval, NSNumber * maxInterval, + CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error)) { + NSLog(@"Subscribe attribute cache called"); + XCTAssertTrue([controller isEqualToString:uuid]); + XCTAssertEqual(nodeId, myNodeId); + XCTAssertNotNil(params); + XCTAssertEqual([params.fabricFiltered boolValue], [myParams.fabricFiltered boolValue]); + XCTAssertEqual([params.keepPreviousSubscriptions boolValue], [myParams.keepPreviousSubscriptions boolValue]); + [callExpectation fulfill]; + completion(nil); + }; _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; [attributeCacheContainer subscribeWithDeviceController:_remoteDeviceController @@ -2303,15 +2363,15 @@ - (void)testSubscribeAttributeCacheFailure __auto_type uuid = self.controllerUUID; __auto_type attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init]; - _handleSubscribeAttributeCache - = ^(id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error)) { - NSLog(@"Subscribe attribute cache called"); - XCTAssertTrue([controller isEqualToString:uuid]); - XCTAssertEqual(nodeId, myNodeId); - XCTAssertNil(params); - [callExpectation fulfill]; - completion(myError); - }; + _handleSubscribeAll = ^(id controller, uint64_t nodeId, NSNumber * minInterval, NSNumber * maxInterval, + CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error)) { + NSLog(@"Subscribe attribute cache called"); + XCTAssertTrue([controller isEqualToString:uuid]); + XCTAssertEqual(nodeId, myNodeId); + XCTAssertNil(params); + [callExpectation fulfill]; + completion(myError); + }; _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; [attributeCacheContainer subscribeWithDeviceController:_remoteDeviceController @@ -2347,14 +2407,14 @@ - (void)testReadAttributeCacheSuccess __auto_type uuid = self.controllerUUID; __auto_type attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init]; - _handleSubscribeAttributeCache - = ^(id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error)) { - NSLog(@"Subscribe attribute cache called"); - XCTAssertTrue([controller isEqualToString:uuid]); - XCTAssertEqual(nodeId, myNodeId); - XCTAssertNil(params); - completion(nil); - }; + _handleSubscribeAll = ^(id controller, uint64_t nodeId, NSNumber * minInterval, NSNumber * maxInterval, + CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error)) { + NSLog(@"Subscribe attribute cache called"); + XCTAssertTrue([controller isEqualToString:uuid]); + XCTAssertEqual(nodeId, myNodeId); + XCTAssertNil(params); + completion(nil); + }; _handleReadAttributeCache = ^(id controller, uint64_t nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId, NSNumber * _Nullable attributeId, void (^completion)(id _Nullable values, NSError * _Nullable error)) { @@ -2409,14 +2469,14 @@ - (void)testReadAttributeCacheFailure __auto_type uuid = self.controllerUUID; __auto_type attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init]; - _handleSubscribeAttributeCache - = ^(id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error)) { - NSLog(@"Subscribe attribute cache called"); - XCTAssertTrue([controller isEqualToString:uuid]); - XCTAssertEqual(nodeId, myNodeId); - XCTAssertNil(params); - completion(nil); - }; + _handleSubscribeAll = ^(id controller, uint64_t nodeId, NSNumber * minInterval, NSNumber * maxInterval, + CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error)) { + NSLog(@"Subscribe attribute cache called"); + XCTAssertTrue([controller isEqualToString:uuid]); + XCTAssertEqual(nodeId, myNodeId); + XCTAssertNil(params); + completion(nil); + }; _handleReadAttributeCache = ^(id controller, uint64_t nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId, NSNumber * _Nullable attributeId, void (^completion)(id _Nullable values, NSError * _Nullable error)) {