From 7c70a256e69d95bff1992e86bba3b62acfce2d03 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Mon, 5 Feb 2024 18:31:46 -0500 Subject: [PATCH] Implement support for more configurable server endpoints in Matter.framework. (#31814) * Implement support for more configurable server endpoints in Matter.framework. * Public APIs on MTRDeviceController to add/remove an endpoint. * Internal APIs on MTRDeviceController to query the access grants for a cluster path and the declared "minimum privilege needed" to read a given attribute. * Changes to the controller factory to stop using app/dynamic_server and instead use the new infrastructure to expose OTA Provider on endpoint 0. * Internal APIs on the controller factory to query access grants and declared privileges. * An implemenation of AccessControl::Delegate to do ACL checks using the above-mentioned APIs. * A fix to MTRServerAttribute's setValue for arrays: it was not expecting the correct data-value structure for an array. This requires fixing some tests too, to provide the right structures. * Changes to the MTRServer* data structures to allow passing nil to associateWithController, to support the OTA endpoint which is not associated with any controller. * Changes to MTRServerCluster to create an AttributeAccessInterface, the set of EmberAfAttributeMetadata needed to represent its attributes, and various other things needed to register a cluster with the "ember" bits. * Changes to MTRServerEndpoint to create an EmberAfEndpointType, a list of EmberAfCluster, and various other things needed to register an endpoint with the "ember" bits. * (Re-)addition of MTRIMDispatch to handle command dispatch for OTA and host a few other functions the "ember" bits expect to exist. * Addition of some headers that the "ember" bits expect to exist at specific paths and with some specific content: "app/PluginApplicationCallbacks.h" and "zap-generated/endpoint_config.h". Importantly, the latter sets FIXED_ENDPOINT_COUNT to 0. * Addition of unit tests that exercise the non-OTA bits of the above (OTA is covered by existing tests), including the ACL checks and so on. * Including a bunch of src/app and src/app/util files needed for the "ember" stuff to work in the framework. * Turning off the chip_build_controller_dynamic_server bit that we are no longer using (and which conflicts with the above bits). * Configure Darwin to support 254 dynamic endpoints (the maximum that makes sense) by default. * Adjusting include paths for the Xcode darwin-framework-tool project, so that it sees the new headers that were added. * Address review comments. * Fix test timeout due to resolving IPv4 non-locahost addresses. * Remove stale comment. --- .../Framework/CHIP/MTRCallbackBridgeBase.h | 2 + .../Framework/CHIP/MTRDeviceController.h | 24 + .../Framework/CHIP/MTRDeviceController.mm | 124 +++++ .../CHIP/MTRDeviceControllerFactory.mm | 162 +++++- .../MTRDeviceControllerFactory_Internal.h | 35 ++ .../CHIP/MTRDeviceController_Internal.h | 19 + .../CHIP/ServerEndpoint/MTRIMDispatch.mm | 104 ++++ .../ServerEndpoint/MTRServerAccessControl.h | 28 ++ .../ServerEndpoint/MTRServerAccessControl.mm | 192 +++++++ .../CHIP/ServerEndpoint/MTRServerAttribute.mm | 9 +- .../MTRServerAttribute_Internal.h | 12 +- .../CHIP/ServerEndpoint/MTRServerCluster.mm | 240 ++++++++- .../MTRServerCluster_Internal.h | 56 ++- .../CHIP/ServerEndpoint/MTRServerEndpoint.h | 5 +- .../CHIP/ServerEndpoint/MTRServerEndpoint.mm | 223 +++++++- .../MTRServerEndpoint_Internal.h | 39 +- .../CHIP/app/PluginApplicationCallbacks.h | 27 + .../CHIP/zap-generated/endpoint_config.h | 51 ++ .../CHIPTests/MTRPerControllerStorageTests.m | 474 ++++++++++++++++++ .../CHIPTests/MTRServerEndpointTests.m | 12 +- .../Matter.xcodeproj/project.pbxproj | 72 ++- .../Framework/chip_xcode_build_connector.sh | 2 +- .../Darwin/CHIPDevicePlatformConfig.h | 5 +- 23 files changed, 1869 insertions(+), 48 deletions(-) create mode 100644 src/darwin/Framework/CHIP/ServerEndpoint/MTRIMDispatch.mm create mode 100644 src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAccessControl.h create mode 100644 src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAccessControl.mm create mode 100644 src/darwin/Framework/CHIP/app/PluginApplicationCallbacks.h create mode 100644 src/darwin/Framework/CHIP/zap-generated/endpoint_config.h diff --git a/src/darwin/Framework/CHIP/MTRCallbackBridgeBase.h b/src/darwin/Framework/CHIP/MTRCallbackBridgeBase.h index ab6565518f1902..d113b5dedc86a1 100644 --- a/src/darwin/Framework/CHIP/MTRCallbackBridgeBase.h +++ b/src/darwin/Framework/CHIP/MTRCallbackBridgeBase.h @@ -215,6 +215,8 @@ class MTRCallbackBridge : public MTRCallbackBridgeBase { } if (!callbackBridge->mQueue) { + ChipLogDetail(Controller, "%s %f seconds: can't dispatch response; no queue", callbackBridge->mCookie.UTF8String, + -[callbackBridge->mRequestTime timeIntervalSinceNow]); if (!callbackBridge->mKeepAlive) { delete callbackBridge; } diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.h b/src/darwin/Framework/CHIP/MTRDeviceController.h index a3757e6353f796..ea2f6497a45edb 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController.h @@ -22,6 +22,7 @@ #import @class MTRBaseDevice; +@class MTRServerEndpoint; // Defined in MTRServerEndpoint.h, which imports MTRAccessGrant.h, which imports MTRBaseClusters.h, which imports this file, so we can't import it. #if MTR_PER_CONTROLLER_STORAGE_ENABLED @class MTRDeviceControllerAbstractParameters; @@ -228,6 +229,29 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) - (NSData * _Nullable)attestationChallengeForDeviceID:(NSNumber *)deviceID MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); +/** + * Add a server endpoint for this controller. The endpoint starts off enabled. + * + * Will fail in the following cases: + * + * 1) There is already an endpoint defined with the given endpoint id. + * 2) There are too many endpoints defined already. + */ +- (BOOL)addServerEndpoint:(MTRServerEndpoint *)endpoint MTR_NEWLY_AVAILABLE; + +/** + * Remove the given server endpoint from this controller. If the endpoint is + * not attached to this controller, will just call the completion and do nothing + * else. + */ +- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint queue:(dispatch_queue_t)queue completion:(dispatch_block_t)completion MTR_NEWLY_AVAILABLE; + +/** + * Remove the given server endpoint without being notified when the removal + * completes. + */ +- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint MTR_NEWLY_AVAILABLE; + /** * Compute a PASE verifier for the desired setup passcode. * diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index decd879a657ef3..9621b447fb49b1 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -41,6 +41,7 @@ #import "MTROperationalCredentialsDelegate.h" #import "MTRP256KeypairBridge.h" #import "MTRPersistentStorageDelegateBridge.h" +#import "MTRServerEndpoint_Internal.h" #import "MTRSetupPayload.h" #import "NSDataSpanConversion.h" #import "NSStringSpanConversion.h" @@ -55,6 +56,7 @@ #include #include +#include #include #include #include @@ -62,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +72,7 @@ #include #include +#include #import @@ -121,6 +125,9 @@ @implementation MTRDeviceController { os_unfair_lock _deviceMapLock; // protects nodeIDToDeviceMap MTRCommissionableBrowser * _commissionableBrowser; MTRAttestationTrustStoreBridge * _attestationTrustStoreBridge; + + // _serverEndpoints is only touched on the Matter queue. + NSMutableArray * _serverEndpoints; } - (nullable instancetype)initWithParameters:(MTRDeviceControllerAbstractParameters *)parameters error:(NSError * __autoreleasing *)error @@ -221,6 +228,7 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory _factory = factory; _deviceMapLock = OS_UNFAIR_LOCK_INIT; _nodeIDToDeviceMap = [NSMutableDictionary dictionary]; + _serverEndpoints = [[NSMutableArray alloc] init]; _commissionableBrowser = nil; _deviceControllerDelegateBridge = new MTRDeviceControllerDelegateBridge(); @@ -285,6 +293,11 @@ - (void)shutDownCppController { assertChipStackLockedByCurrentThread(); + // Shut down all our endpoints. + for (MTRServerEndpoint * endpoint in [_serverEndpoints copy]) { + [self removeServerEndpointOnMatterQueue:endpoint]; + } + if (_cppCommissioner) { auto * commissionerToShutDown = _cppCommissioner; // Flag ourselves as not running before we start shutting down @@ -947,6 +960,71 @@ - (NSData * _Nullable)attestationChallengeForDeviceID:(NSNumber *)deviceID return [self syncRunOnWorkQueueWithReturnValue:block error:nil]; } +- (BOOL)addServerEndpoint:(MTRServerEndpoint *)endpoint +{ + VerifyOrReturnValue([self checkIsRunning], NO); + + if (![_factory addServerEndpoint:endpoint]) { + return NO; + } + + if (![endpoint associateWithController:self]) { + MTR_LOG_ERROR("Failed to associate MTRServerEndpoint with MTRDeviceController"); + [_factory removeServerEndpoint:endpoint]; + return NO; + } + + [self asyncDispatchToMatterQueue:^() { + [self->_serverEndpoints addObject:endpoint]; + [endpoint registerMatterEndpoint]; + } + errorHandler:^(NSError * error) { + MTR_LOG_ERROR("Unexpected failure dispatching to Matter queue on running controller in addServerEndpoint"); + }]; + return YES; +} + +- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint queue:(dispatch_queue_t)queue completion:(dispatch_block_t)completion +{ + [self removeServerEndpointInternal:endpoint queue:queue completion:completion]; +} + +- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint +{ + [self removeServerEndpointInternal:endpoint queue:nil completion:nil]; +} + +- (void)removeServerEndpointInternal:(MTRServerEndpoint *)endpoint queue:(dispatch_queue_t _Nullable)queue completion:(dispatch_block_t _Nullable)completion +{ + VerifyOrReturn([self checkIsRunning]); + + // We need to unhook the endpoint from the Matter side before we can start + // tearing it down. + [self asyncDispatchToMatterQueue:^() { + [self removeServerEndpointOnMatterQueue:endpoint]; + if (queue != nil && completion != nil) { + dispatch_async(queue, completion); + } + } + errorHandler:^(NSError * error) { + // Error means we got shut down, so the endpoint is removed now. + if (queue != nil && completion != nil) { + dispatch_async(queue, completion); + } + }]; +} + +- (void)removeServerEndpointOnMatterQueue:(MTRServerEndpoint *)endpoint +{ + assertChipStackLockedByCurrentThread(); + + [endpoint unregisterMatterEndpoint]; + [_serverEndpoints removeObject:endpoint]; + [endpoint invalidate]; + + [_factory removeServerEndpoint:endpoint]; +} + - (BOOL)checkForInitError:(BOOL)condition logMsg:(NSString *)logMsg { if (condition) { @@ -1230,6 +1308,52 @@ - (void)downloadLogFromNodeWithID:(NSNumber *)nodeID completion:completion]; } +- (NSArray *)accessGrantsForClusterPath:(MTRClusterPath *)clusterPath +{ + assertChipStackLockedByCurrentThread(); + + for (MTRServerEndpoint * endpoint in _serverEndpoints) { + if ([clusterPath.endpoint isEqual:endpoint.endpointID]) { + return [endpoint matterAccessGrantsForCluster:clusterPath.cluster]; + } + } + + // Nothing matched, no grants. + return @[]; +} + +- (nullable NSNumber *)neededReadPrivilegeForClusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID +{ + assertChipStackLockedByCurrentThread(); + + for (MTRServerEndpoint * endpoint in _serverEndpoints) { + for (MTRServerCluster * cluster in endpoint.serverClusters) { + if (![cluster.clusterID isEqual:clusterID]) { + continue; + } + + for (MTRServerAttribute * attr in cluster.attributes) { + if (![attr.attributeID isEqual:attributeID]) { + continue; + } + + return @(attr.requiredReadPrivilege); + } + } + } + + return nil; +} + +#ifdef DEBUG ++ (void)forceLocalhostAdvertisingOnly +{ + auto interfaceIndex = chip::Inet::InterfaceId::PlatformType(kDNSServiceInterfaceIndexLocalOnly); + auto interfaceId = chip::Inet::InterfaceId(interfaceIndex); + chip::app::DnssdServer::Instance().SetInterfaceId(interfaceId); +} +#endif // DEBUG + @end /** diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm index 319c33fdac999e..457abeaed55a5f 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm @@ -25,6 +25,9 @@ #import "MTRDeviceControllerParameters_Wrapper.h" #endif // MTR_PER_CONTROLLER_STORAGE_ENABLED +#import +#import + #import "MTRCertificates.h" #import "MTRDemuxingStorage.h" #import "MTRDeviceController.h" @@ -40,12 +43,15 @@ #import "MTROperationalBrowser.h" #import "MTRP256KeypairBridge.h" #import "MTRPersistentStorageDelegateBridge.h" +#import "MTRServerAccessControl.h" +#import "MTRServerCluster_Internal.h" +#import "MTRServerEndpoint_Internal.h" #import "MTRSessionResumptionStorageBridge.h" #import "NSDataSpanConversion.h" #import -#include +#include #include #include #include @@ -75,12 +81,14 @@ static bool sExitHandlerRegistered = false; static void ShutdownOnExit() { [[MTRDeviceControllerFactory sharedInstance] stopControllerFactory]; } -@interface MTRDeviceControllerFactory () +@interface MTRDeviceControllerFactory () { + MTRServerEndpoint * _otaProviderEndpoint; + std::unique_ptr _otaProviderDelegateBridge; +} @property (atomic, readonly) dispatch_queue_t chipWorkQueue; @property (readonly) DeviceControllerFactory * controllerFactory; @property (readonly) PersistentStorageDelegate * persistentStorageDelegate; -@property (readonly) MTROTAProviderDelegateBridge * otaProviderDelegateBridge; @property (readonly) Crypto::RawKeySessionKeystore * sessionKeystore; // We use TestPersistentStorageDelegate just to get an in-memory store to back // our group data provider impl. We initialize this store correctly on every @@ -170,6 +178,11 @@ @implementation MTRDeviceControllerFactory { // is only accessed on the Matter queue or after the Matter queue has shut // down. FabricIndex _nextAvailableFabricIndex; + + // Array of all server endpoints across all controllers, used to ensure + // in an atomic way that endpoint IDs are unique. + NSMutableArray * _serverEndpoints; + os_unfair_lock _serverEndpointsLock; // Protects access to _serverEndpoints. } + (void)initialize @@ -232,6 +245,9 @@ - (instancetype)init return nil; } + _serverEndpoints = [[NSMutableArray alloc] init]; + _serverEndpointsLock = OS_UNFAIR_LOCK_INIT; + return self; } @@ -319,10 +335,6 @@ - (void)cleanupStartupObjects _keystore = nullptr; } - if (_otaProviderDelegateBridge) { - delete _otaProviderDelegateBridge; - _otaProviderDelegateBridge = nullptr; - } _otaProviderDelegateQueue = nil; _otaProviderDelegate = nil; @@ -410,7 +422,7 @@ - (BOOL)startControllerFactory:(MTRDeviceControllerFactoryParams *)startupParams return; } - app::dynamic_server::InitAccessControl(); + InitializeServerAccessControl(); if (startupParams.hasStorage) { _persistentStorageDelegate = new (std::nothrow) MTRPersistentStorageDelegateBridge(startupParams.storage); @@ -439,7 +451,6 @@ - (BOOL)startControllerFactory:(MTRDeviceControllerFactoryParams *)startupParams _otaProviderDelegateQueue = dispatch_queue_create( "org.csa-iot.matter.framework.otaprovider.workqueue", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); } - _otaProviderDelegateBridge = new MTROTAProviderDelegateBridge(); // TODO: Allow passing a different keystore implementation via startupParams. _keystore = new PersistentStorageOperationalKeystore(); @@ -900,11 +911,44 @@ - (MTRDeviceController * _Nullable)maybeInitializeOTAProvider:(MTRDeviceControll { [self _assertCurrentQueueIsNotMatterQueue]; - VerifyOrReturnValue(_otaProviderDelegateBridge != nil, controller); VerifyOrReturnValue([_controllers count] == 1, controller); + _otaProviderEndpoint = [MTRServerEndpoint rootNodeEndpoint]; + + // TODO: Have the OTA Provider cluster revision accessible somewhere? + auto * otaProviderCluster = [[MTRServerCluster alloc] initWithClusterID:@(MTRClusterIDTypeOTASoftwareUpdateProviderID) revision:@(1)]; + otaProviderCluster.acceptedCommands = @[ + @(MTRCommandIDTypeClusterOTASoftwareUpdateProviderCommandQueryImageID), + @(MTRCommandIDTypeClusterOTASoftwareUpdateProviderCommandApplyUpdateRequestID), + @(MTRCommandIDTypeClusterOTASoftwareUpdateProviderCommandNotifyUpdateAppliedID), + ]; + otaProviderCluster.generatedCommands = @[ + @(MTRCommandIDTypeClusterOTASoftwareUpdateProviderCommandQueryImageResponseID), + @(MTRCommandIDTypeClusterOTASoftwareUpdateProviderCommandApplyUpdateResponseID), + ]; + [otaProviderCluster addAccessGrant:[MTRAccessGrant accessGrantForAllNodesWithPrivilege:MTRAccessControlEntryPrivilegeOperate]]; + + // Not expected to fail, since we are following the rules for clusters here. + [_otaProviderEndpoint addServerCluster:otaProviderCluster]; + + if (![self addServerEndpoint:_otaProviderEndpoint]) { + MTR_LOG_ERROR("Failed to add OTA endpoint on factory. Why?"); + [controller shutdown]; + return nil; + } + + // This endpoint is not actually associated with a specific controller; we + // just need to have a working Matter event loop to bring it up. + [_otaProviderEndpoint associateWithController:nil]; + __block CHIP_ERROR err; dispatch_sync(_chipWorkQueue, ^{ + [self->_otaProviderEndpoint registerMatterEndpoint]; + + // Now that our endpoint exists, go ahead and create the OTA delegate + // bridge. Its constructor relies on the endpoint existing. + _otaProviderDelegateBridge = std::make_unique(); + auto systemState = _controllerFactory->GetSystemState(); err = _otaProviderDelegateBridge->Init(systemState->SystemLayer(), systemState->ExchangeMgr()); }); @@ -982,6 +1026,16 @@ - (void)controllerShuttingDown:(MTRDeviceController *)controller if (_otaProviderDelegateBridge) { _otaProviderDelegateBridge->Shutdown(); + _otaProviderDelegateBridge.reset(); + } + + if (_otaProviderEndpoint != nil) { + [_otaProviderEndpoint unregisterMatterEndpoint]; + [_otaProviderEndpoint invalidate]; + + [self removeServerEndpoint:_otaProviderEndpoint]; + + _otaProviderEndpoint = nil; } sharedCleanupBlock(); @@ -1071,6 +1125,94 @@ - (nullable MTRDeviceController *)runningControllerForFabricIndex:(chip::FabricI return [self runningControllerForFabricIndex:fabricIndex includeControllerStartingUp:YES includeControllerShuttingDown:YES]; } +- (BOOL)addServerEndpoint:(MTRServerEndpoint *)endpoint +{ + os_unfair_lock_lock(&_serverEndpointsLock); + if (_serverEndpoints.count == CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) { + os_unfair_lock_unlock(&_serverEndpointsLock); + + MTR_LOG_ERROR("Can't add a server endpoint with endpoint ID %u, because we already have %u endpoints defined", static_cast(endpoint.endpointID.unsignedLongLongValue), CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT); + + return NO; + } + + BOOL haveExisting = NO; + for (MTRServerEndpoint * existing in _serverEndpoints) { + if ([endpoint.endpointID isEqual:existing.endpointID]) { + haveExisting = YES; + break; + } + } + + if (!haveExisting) { + [_serverEndpoints addObject:endpoint]; + } + os_unfair_lock_unlock(&_serverEndpointsLock); + + if (haveExisting) { + MTR_LOG_ERROR("Trying to add a server endpoint with endpoint ID %u, which already exists", static_cast(endpoint.endpointID.unsignedLongLongValue)); + } + + return !haveExisting; +} + +- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint +{ + os_unfair_lock_lock(&_serverEndpointsLock); + [_serverEndpoints removeObject:endpoint]; + os_unfair_lock_unlock(&_serverEndpointsLock); +} + +- (NSArray *)accessGrantsForFabricIndex:(chip::FabricIndex)fabricIndex clusterPath:(MTRClusterPath *)clusterPath +{ + assertChipStackLockedByCurrentThread(); + + if ([clusterPath.endpoint isEqual:_otaProviderEndpoint.endpointID]) { + return [_otaProviderEndpoint matterAccessGrantsForCluster:clusterPath.cluster]; + } + + // We do not want to use _serverEndpoints here, because that might contain + // endpoints that are still being set up and whatnot. Ask the controller + // for the relevant fabric index what the relevant access grants are. + + // Include controllers that are shutting down, since this may be an accesss + // check for event reports they emit as they shut down. + auto * controller = [self runningControllerForFabricIndex:fabricIndex includeControllerStartingUp:NO includeControllerShuttingDown:YES]; + if (controller == nil) { + return @[]; + } + + return [controller accessGrantsForClusterPath:clusterPath]; +} + +- (nullable NSNumber *)neededReadPrivilegeForClusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID +{ + assertChipStackLockedByCurrentThread(); + + for (MTRServerCluster * cluster in _otaProviderEndpoint.serverClusters) { + if (![cluster.clusterID isEqual:clusterID]) { + continue; + } + + for (MTRServerAttribute * attr in cluster.attributes) { + if (![attr.attributeID isEqual:attributeID]) { + continue; + } + + return @(attr.requiredReadPrivilege); + } + } + + for (MTRDeviceController * controller in [self getRunningControllers]) { + NSNumber * _Nullable neededPrivilege = [controller neededReadPrivilegeForClusterID:clusterID attributeID:attributeID]; + if (neededPrivilege != nil) { + return neededPrivilege; + } + } + + return nil; +} + - (void)downloadLogFromNodeWithID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller type:(MTRDiagnosticLogType)type diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory_Internal.h index e41300127be59e..4e0290bc041860 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory_Internal.h @@ -20,9 +20,12 @@ */ #import +#import +#import // for MTRClusterPath #import #import #import +#import #if MTR_PER_CONTROLLER_STORAGE_ENABLED #import @@ -93,6 +96,38 @@ NS_ASSUME_NONNULL_BEGIN withParameters:(MTRDeviceControllerParameters *)parameters error:(NSError * __autoreleasing *)error; +/** + * Add a server endpoint. This will verify that there is no existing server + * endpoint with the provided endpoint ID and return NO if there is one. Can be + * called on any thread. + */ +- (BOOL)addServerEndpoint:(MTRServerEndpoint *)endpoint; + +/** + * Remove a server endpoint. This must happen after all other teardown for the + * endpoint is complete. Can be called on any thread. + */ +- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint; + +/** + * Get the access grants that apply for the given fabric index and cluster path. + * + * Only called on the Matter queue. + */ +- (NSArray *)accessGrantsForFabricIndex:(chip::FabricIndex)fabricIndex clusterPath:(MTRClusterPath *)clusterPath; + +/** + * Get the privilege level needed to read the given attribute. There's no + * endpoint provided because the expectation is that this information is the + * same for all cluster instances. + * + * Returns nil if we have no such attribute defined on any endpoint, otherwise + * one of MTRAccessControlEntry* constants wrapped in NSNumber. + * + * Only called on the Matter queue. + */ +- (nullable NSNumber *)neededReadPrivilegeForClusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID; + @property (readonly) chip::PersistentStorageDelegate * storageDelegate; @property (readonly) chip::Credentials::GroupDataProvider * groupData; diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h index 92873cf4016b9e..b55d41b8d8a59e 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h @@ -20,6 +20,8 @@ */ #import +#import +#import // for MTRClusterPath #import "MTRDeviceConnectionBridge.h" // For MTRInternalDeviceConnectionCallback #import "MTRDeviceController.h" @@ -243,6 +245,23 @@ NS_ASSUME_NONNULL_BEGIN queue:(dispatch_queue_t)queue completion:(void (^)(NSURL * _Nullable url, NSError * _Nullable error))completion; +/** + * Get the access grants that apply for the given cluster path. + */ +- (NSArray *)accessGrantsForClusterPath:(MTRClusterPath *)clusterPath; + +/** + * Get the privilege level needed to read the given attribute. There's no + * endpoint provided because the expectation is that this information is the + * same for all cluster instances. + * + * Returns nil if we have no such attribute defined on any endpoint, otherwise + * one of MTRAccessControlEntry* constants wrapped in NSNumber. + * + * Only called on the Matter queue. + */ +- (nullable NSNumber *)neededReadPrivilegeForClusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID; + #pragma mark - Device-specific data and SDK access // DeviceController will act as a central repository for this opaque dictionary that MTRDevice manages - (MTRDevice *)deviceForNodeID:(NSNumber *)nodeID; diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRIMDispatch.mm b/src/darwin/Framework/CHIP/ServerEndpoint/MTRIMDispatch.mm new file mode 100644 index 00000000000000..a1f1f47a7dc26e --- /dev/null +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRIMDispatch.mm @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2024 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; + +void emberAfClusterInitCallback(EndpointId endpoint, ClusterId clusterId) +{ + assertChipStackLockedByCurrentThread(); + + // No-op: Descriptor and OTA do not need this, and our client-defined + // clusters dont use it. +} + +EmberAfStatus emAfWriteAttributeExternal(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr, + EmberAfAttributeType dataType) +{ + assertChipStackLockedByCurrentThread(); + + // All of our attributes are handled via AttributeAccessInterface, so this + // should be unreached. + return EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE; +} + +namespace chip { +namespace app { + + void DispatchSingleClusterCommand(const ConcreteCommandPath & aPath, TLV::TLVReader & aReader, CommandHandler * aCommandObj) + { + // TODO: Consider having MTRServerCluster register a + // CommandHandlerInterface for command dispatch. But OTA would need + // some special-casing in any case, to call into the existing cluster + // implementation. + using Protocols::InteractionModel::Status; + // This command passed ServerClusterCommandExists so we know it's one of our + // supported commands. + using namespace OtaSoftwareUpdateProvider::Commands; + + bool wasHandled = false; + CHIP_ERROR err = CHIP_NO_ERROR; + + switch (aPath.mCommandId) { + case QueryImage::Id: { + QueryImage::DecodableType commandData; + err = DataModel::Decode(aReader, commandData); + if (err == CHIP_NO_ERROR) { + wasHandled = emberAfOtaSoftwareUpdateProviderClusterQueryImageCallback(aCommandObj, aPath, commandData); + } + break; + } + case ApplyUpdateRequest::Id: { + ApplyUpdateRequest::DecodableType commandData; + err = DataModel::Decode(aReader, commandData); + if (err == CHIP_NO_ERROR) { + wasHandled = emberAfOtaSoftwareUpdateProviderClusterApplyUpdateRequestCallback(aCommandObj, aPath, commandData); + } + break; + } + case NotifyUpdateApplied::Id: { + NotifyUpdateApplied::DecodableType commandData; + err = DataModel::Decode(aReader, commandData); + if (err == CHIP_NO_ERROR) { + wasHandled = emberAfOtaSoftwareUpdateProviderClusterNotifyUpdateAppliedCallback(aCommandObj, aPath, commandData); + } + break; + } + default: + break; + } + + if (CHIP_NO_ERROR != err || !wasHandled) { + aCommandObj->AddStatus(aPath, Status::InvalidCommand); + } + } + +} // namespace app +} // namespace chip diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAccessControl.h b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAccessControl.h new file mode 100644 index 00000000000000..49d542a38ce745 --- /dev/null +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAccessControl.h @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2024 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 +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Initialize the access control module. Must be called on the Matter task + * queue. + */ +void InitializeServerAccessControl(); + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAccessControl.mm b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAccessControl.mm new file mode 100644 index 00000000000000..984716511a7495 --- /dev/null +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAccessControl.mm @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2024 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 "MTRServerAccessControl.h" + +#import +#import // for MTRClusterPath +#import + +#import "MTRDeviceControllerFactory_Internal.h" +#import "MTRLogging_Internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace chip; +using namespace chip::Access; + +namespace { + +class DeviceTypeResolver : public Access::AccessControl::DeviceTypeResolver { +public: + bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint) override + { + return app::IsDeviceTypeOnEndpoint(deviceType, endpoint); + } +}; + +class AccessControlDelegate : public AccessControl::Delegate { + CHIP_ERROR Check(const SubjectDescriptor & subjectDescriptor, const RequestPath & requestPath, + Privilege requestPrivilege) override + { + auto * clusterPath = [MTRClusterPath clusterPathWithEndpointID:@(requestPath.endpoint) clusterID:@(requestPath.cluster)]; + auto * grants = [[MTRDeviceControllerFactory sharedInstance] accessGrantsForFabricIndex:subjectDescriptor.fabricIndex clusterPath:clusterPath]; + + for (MTRAccessGrant * grant in grants) { + if (!GrantSubjectMatchesDescriptor(grant, subjectDescriptor)) { + continue; + } + + // Check whether the desired privilege is granted. See the Access Control "Overall + // Algorithm" section in the spec for which privileges imply which other privileges. + switch (grant.grantedPrivilege) { + case MTRAccessControlEntryPrivilegeView: + if (requestPrivilege == Privilege::kView) { + return CHIP_NO_ERROR; + } + break; + case MTRAccessControlEntryPrivilegeProxyView: + if (requestPrivilege == Privilege::kView || requestPrivilege == Privilege::kProxyView) { + return CHIP_NO_ERROR; + } + break; + case MTRAccessControlEntryPrivilegeOperate: + if (requestPrivilege == Privilege::kView || requestPrivilege == Privilege::kOperate) { + return CHIP_NO_ERROR; + } + break; + case MTRAccessControlEntryPrivilegeManage: + if (requestPrivilege == Privilege::kView || requestPrivilege == Privilege::kOperate || requestPrivilege == Privilege::kManage) { + return CHIP_NO_ERROR; + } + break; + case MTRAccessControlEntryPrivilegeAdminister: + if (requestPrivilege == Privilege::kView || requestPrivilege == Privilege::kProxyView || requestPrivilege == Privilege::kOperate || requestPrivilege == Privilege::kManage || requestPrivilege == Privilege::kAdminister) { + return CHIP_NO_ERROR; + } + break; + default: + MTR_LOG_ERROR("Uknown granted privilege %u, ignoring", grant.grantedPrivilege); + break; + } + + // If this grant did not match, just move on to the next one. + } + + // None of the grants matched. + return CHIP_ERROR_ACCESS_DENIED; + } + + bool GrantSubjectMatchesDescriptor(MTRAccessGrant * grant, const SubjectDescriptor & descriptor) + { + if (grant.subjectID == nil) { + // This is an all-nodes grant for CASE access only. + return descriptor.authMode == AuthMode::kCase; + } + + NodeId grantSubjectNodeId = grant.subjectID.unsignedLongLongValue; + if (IsOperationalNodeId(grantSubjectNodeId)) { + return descriptor.authMode == AuthMode::kCase && descriptor.subject == grantSubjectNodeId; + } + + if (IsGroupId(grantSubjectNodeId)) { + return descriptor.authMode == AuthMode::kGroup && descriptor.subject == grantSubjectNodeId; + } + + if (IsCASEAuthTag(grantSubjectNodeId)) { + return descriptor.cats.CheckSubjectAgainstCATs(grantSubjectNodeId); + } + + MTR_LOG_ERROR("Unexpected grant subject: 0x%llx", grantSubjectNodeId); + return false; + } +}; + +struct ControllerAccessControl { + DeviceTypeResolver mDeviceTypeResolver; + AccessControlDelegate mDelegate; + ControllerAccessControl() { GetAccessControl().Init(&mDelegate, mDeviceTypeResolver); } +}; + +Global gControllerAccessControl; + +} // anonymous namespace + +int MatterGetAccessPrivilegeForReadEvent(ClusterId cluster, EventId event) +{ + // We don't support any event bits yet. + return kMatterAccessPrivilegeAdminister; +} + +int MatterGetAccessPrivilegeForInvokeCommand(ClusterId cluster, CommandId command) +{ + // For now we only have OTA, which uses Operate. + return kMatterAccessPrivilegeOperate; +} + +int MatterGetAccessPrivilegeForReadAttribute(ClusterId cluster, AttributeId attribute) +{ + NSNumber * _Nullable neededPrivilege = [[MTRDeviceControllerFactory sharedInstance] neededReadPrivilegeForClusterID:@(cluster) attributeID:@(attribute)]; + if (neededPrivilege == nil) { + // No privileges declared for this attribute on this cluster. Treat as + // "needs admin privileges", so we fail closed. + return kMatterAccessPrivilegeAdminister; + } + + switch (neededPrivilege.unsignedLongLongValue) { + case MTRAccessControlEntryPrivilegeView: + return kMatterAccessPrivilegeView; + case MTRAccessControlEntryPrivilegeOperate: + return kMatterAccessPrivilegeOperate; + case MTRAccessControlEntryPrivilegeManage: + return kMatterAccessPrivilegeManage; + case MTRAccessControlEntryPrivilegeAdminister: + return kMatterAccessPrivilegeAdminister; + case MTRAccessControlEntryPrivilegeProxyView: + // Just treat this as an unknown value; there is no value for this in privilege-storage. + FALLTHROUGH; + default: + break; + } + + // To be safe, treat unknown values as "needs admin privileges". That way the failure case + // disallows access that maybe should be allowed, instead of allowing access that maybe + // should be disallowed. + return kMatterAccessPrivilegeAdminister; +} + +int MatterGetAccessPrivilegeForWriteAttribute(ClusterId cluster, AttributeId attribute) +{ + // We don't have any writable attributes yet, but default to Operate. + return kMatterAccessPrivilegeOperate; +} + +void InitializeServerAccessControl() +{ + assertChipStackLockedByCurrentThread(); + + // Ensure the access control bits are created. No-op after the first call. + gControllerAccessControl.get(); +} diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute.mm b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute.mm index 0337d555d02f2a..22fd2679894f15 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute.mm +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute.mm @@ -95,8 +95,13 @@ - (BOOL)setValue:(NSDictionary *)value return NO; } for (id item in dataValueList) { + if (![item isKindOfClass:NSDictionary.class]) { + MTR_LOG_ERROR("MTRServerAttribute value array should contain dictionaries"); + } + NSDictionary * itemDictionary = item; + NSError * encodingError; - NSData * encodedItem = MTREncodeTLVFromDataValueDictionary(item, &encodingError); + NSData * encodedItem = MTREncodeTLVFromDataValueDictionary(itemDictionary[MTRDataKey], &encodingError); if (encodedItem == nil) { return NO; } @@ -132,7 +137,7 @@ - (BOOL)setValue:(NSDictionary *)value return YES; } -- (BOOL)associateWithController:(MTRDeviceController *)controller +- (BOOL)associateWithController:(nullable MTRDeviceController *)controller { MTRDeviceController * existingController = _deviceController; if (existingController != nil) { diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute_Internal.h b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute_Internal.h index dbc7cebb857a88..f6b423fd628470 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute_Internal.h +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute_Internal.h @@ -21,15 +21,19 @@ #include +NS_ASSUME_NONNULL_BEGIN + @interface MTRServerAttribute () /** - * Mark this attribute as associated with a particular controller. + * Mark this attribute as associated with a particular controller. The + * controller can be nil to indicate that the endpoint is not associated with a + * specific controller but rather with the controller factory. */ -- (BOOL)associateWithController:(MTRDeviceController *)controller; +- (BOOL)associateWithController:(nullable MTRDeviceController *)controller; /** - * Mark this attribute as part of an Defunct-state endpoint. + * Mark this attribute as part of an endpoint that is no longer being used. */ - (void)invalidate; @@ -46,3 +50,5 @@ @property (nonatomic, assign) chip::app::ConcreteClusterPath parentCluster; @end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm index 8af4ca975ffb3d..8de78a675e7eb2 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster.mm @@ -23,12 +23,40 @@ #import #import +#import "NSDataSpanConversion.h" + +#include #include +#include #include #include +#include #include +#include + +// TODO: These attribute-*.h bits are a hack that should eventually go away. +#include +#include using namespace chip; +using namespace chip::app; + +class MTRServerAttributeAccessInterface : public AttributeAccessInterface { +public: + MTRServerAttributeAccessInterface(EndpointId aEndpointID, ClusterId aClusterID, NSArray * aAttributes, + NSNumber * aClusterRevision) + : AttributeAccessInterface(MakeOptional(aEndpointID), aClusterID) + , mAttributes(aAttributes) + , mClusterRevision(aClusterRevision) + { + } + + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + +private: + NSArray * mAttributes; + NSNumber * mClusterRevision; +}; MTR_DIRECT_MEMBERS @implementation MTRServerCluster { @@ -39,6 +67,15 @@ @implementation MTRServerCluster { NSMutableSet * _accessGrants; NSMutableArray * _attributes; MTRDeviceController * __weak _deviceController; + + std::unique_ptr _attributeAccessInterface; + // We can't use something like std::unique_ptr + // because EmberAfAttributeMetadata does not have a default constructor, so + // we can't alloc and then initializer later. + std::vector _matterAttributeMetadata; + + std::unique_ptr _matterAcceptedCommandList; + std::unique_ptr _matterGeneratedCommandList; } - (nullable instancetype)initWithClusterID:(NSNumber *)clusterID revision:(NSNumber *)revision @@ -71,7 +108,7 @@ - (nullable instancetype)initWithClusterID:(NSNumber *)clusterID revision:(NSNum + (MTRServerCluster *)newDescriptorCluster { - return [[MTRServerCluster alloc] initInternalWithClusterID:@(MTRClusterIDTypeDescriptorID) revision:@(app::Clusters::Descriptor::kClusterRevision) accessGrants:[NSSet set] attributes:@[]]; + return [[MTRServerCluster alloc] initInternalWithClusterID:@(MTRClusterIDTypeDescriptorID) revision:@(Clusters::Descriptor::kClusterRevision) accessGrants:[NSSet set] attributes:@[]]; } - (instancetype)initInternalWithClusterID:(NSNumber *)clusterID revision:(NSNumber *)revision accessGrants:(NSSet *)accessGrants attributes:(NSArray *)attributes @@ -165,11 +202,18 @@ - (BOOL)addAttribute:(MTRServerAttribute *)attribute } [_attributes addObject:attribute]; - attribute.parentCluster = app::ConcreteClusterPath(_parentEndpoint, static_cast(_clusterID.unsignedLongLongValue)); + attribute.parentCluster = ConcreteClusterPath(_parentEndpoint, static_cast(_clusterID.unsignedLongLongValue)); return YES; } -- (BOOL)associateWithController:(MTRDeviceController *)controller +static constexpr EmberAfAttributeMetadata sDescriptorAttributesMetadata[] = { + DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeClusterDescriptorAttributeDeviceTypeListID, ARRAY, 0, 0), + DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeClusterDescriptorAttributeServerListID, ARRAY, 0, 0), + DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeClusterDescriptorAttributeClientListID, ARRAY, 0, 0), + DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeClusterDescriptorAttributePartsListID, ARRAY, 0, 0), +}; + +- (BOOL)associateWithController:(nullable MTRDeviceController *)controller { MTRDeviceController * existingController = _deviceController; if (existingController != nil) { @@ -191,6 +235,84 @@ - (BOOL)associateWithController:(MTRDeviceController *)controller // Snapshot _matterAccessGrants now; after this point it will only be // updated on the Matter queue. _matterAccessGrants = [_accessGrants copy]; + + // _attributes shouldn't be able to change anymore, so we can now construct + // our EmberAfAttributeMetadata array. + size_t attributeCount = _attributes.count; + + // Figure out whether we need to synthesize a FeatureMap attribute. + bool needsFeatureMap = true; + for (MTRServerAttribute * attr in _attributes) { + if ([attr.attributeID isEqual:@(MTRClusterGlobalAttributeFeatureMapID)]) { + needsFeatureMap = false; + break; + } + } + + bool needsDescriptorAttributes = [_clusterID isEqual:@(MTRClusterIDTypeDescriptorID)]; + + if (needsFeatureMap) { + ++attributeCount; + } + + if (needsDescriptorAttributes) { + attributeCount += ArraySize(sDescriptorAttributesMetadata); + } + + // And add one for ClusterRevision + ++attributeCount; + + if (attributeCount >= UINT16_MAX) { + MTR_LOG_ERROR("Unable to have %llu attributes in a single cluster (clusterID: " ChipLogFormatMEI ")", + static_cast(attributeCount), ChipLogValueMEI(_clusterID.unsignedLongLongValue)); + return NO; + } + + size_t attrIndex = 0; + for (; attrIndex < _attributes.count; ++attrIndex) { + auto * attr = _attributes[attrIndex]; + _matterAttributeMetadata.emplace_back(EmberAfAttributeMetadata(DECLARE_DYNAMIC_ATTRIBUTE(static_cast(attr.attributeID.unsignedLongLongValue), + // The type does not actually matter, since we plan to + // handle this entirely via AttributeAccessInterface. + // Claim Array because that one will keep random IM + // code from trying to do things with the attribute + // store. + ARRAY, + // Size in bytes does not matter, since we plan to + // handle this entirely via AttributeAccessInterface. + 0, + // ATTRIBUTE_MASK_NULLABLE is not relevant because we + // are handling this all via AttributeAccessInterface. + 0))); + } + + if (needsFeatureMap) { + _matterAttributeMetadata.emplace_back(EmberAfAttributeMetadata(DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeGlobalAttributeFeatureMapID, + BITMAP32, 4, 0))); + ++attrIndex; + } + + if (needsDescriptorAttributes) { + for (auto & data : sDescriptorAttributesMetadata) { + _matterAttributeMetadata.emplace_back(data); + ++attrIndex; + } + } + + // Add our ClusterRevision bit. + _matterAttributeMetadata.emplace_back(EmberAfAttributeMetadata(DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeGlobalAttributeClusterRevisionID, + INT16U, 2, 0))); + ++attrIndex; + + _attributeAccessInterface = std::make_unique(_parentEndpoint, + static_cast(_clusterID.unsignedLongLongValue), + _attributes, + _clusterRevision); + // _attributeAccessInterface needs to be registered on the Matter queue; that will happen later. + + _matterAcceptedCommandList = [MTRServerCluster makeMatterCommandList:_acceptedCommands]; + _matterGeneratedCommandList = [MTRServerCluster makeMatterCommandList:_generatedCommands]; + _deviceController = controller; return YES; @@ -198,13 +320,44 @@ - (BOOL)associateWithController:(MTRDeviceController *)controller - (void)invalidate { + // Undo any work associateWithController did. for (MTRServerAttribute * attr in _attributes) { [attr invalidate]; } + // We generally promise to only touch _matterAccessGrants on the Matter + // queue after associateWithController succeeds, but we are no longer being + // looked at from that queue, so it's safe to reset it here. + _matterAccessGrants = [NSSet set]; + _matterAttributeMetadata.clear(); + _attributeAccessInterface.reset(); + _matterAcceptedCommandList.reset(); + _matterGeneratedCommandList.reset(); + _deviceController = nil; } +- (void)registerMatterCluster +{ + assertChipStackLockedByCurrentThread(); + + if (!registerAttributeAccessOverride(_attributeAccessInterface.get())) { + // This should only happen if we somehow managed to register an + // AttributeAccessInterface for the same (endpoint, cluster) pair. + MTR_LOG_ERROR("Could not register AttributeAccessInterface for endpoint %u, cluster 0x%llx", + _parentEndpoint, _clusterID.unsignedLongLongValue); + } +} + +- (void)unregisterMatterCluster +{ + assertChipStackLockedByCurrentThread(); + + if (_attributeAccessInterface != nullptr) { + unregisterAttributeAccessOverride(_attributeAccessInterface.get()); + } +} + - (NSArray *)accessGrants { return [_accessGrants allObjects]; @@ -221,8 +374,87 @@ - (void)setParentEndpoint:(EndpointId)endpoint // Update it on all the attributes, in case the attributes were added to us // before we were added to the endpoint. for (MTRServerAttribute * attr in _attributes) { - attr.parentCluster = app::ConcreteClusterPath(endpoint, static_cast(_clusterID.unsignedLongLongValue)); + attr.parentCluster = ConcreteClusterPath(endpoint, static_cast(_clusterID.unsignedLongLongValue)); } } +- (Span)matterAttributeMetadata +{ + // This is always called after our _matterAttributeMetadata has been set up + // by associateWithController. + return Span(_matterAttributeMetadata.data(), _matterAttributeMetadata.size()); +} + +- (CommandId *)matterAcceptedCommands +{ + return _matterAcceptedCommandList.get(); +} + +- (CommandId *)matterGeneratedCommands +{ + return _matterGeneratedCommandList.get(); +} + ++ (std::unique_ptr)makeMatterCommandList:(NSArray * _Nullable)commandList +{ + if (commandList.count == 0) { + return nullptr; + } + + // Lists of accepted/generated commands are terminated by kInvalidClusterId. + auto matterCommandList = std::make_unique(commandList.count + 1); + for (size_t index = 0; index < commandList.count; ++index) { + matterCommandList[index] = static_cast(commandList[index].unsignedLongLongValue); + } + matterCommandList[commandList.count] = kInvalidClusterId; + return matterCommandList; +} + @end + +CHIP_ERROR MTRServerAttributeAccessInterface::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + using DataModel::PreEncodedValue; + + // Find the right attribute in our list. + MTRServerAttribute * foundAttr = nil; + for (MTRServerAttribute * attr in mAttributes) { + if ([attr.attributeID isEqual:@(aPath.mAttributeId)]) { + foundAttr = attr; + break; + } + } + + if (foundAttr) { + id value = foundAttr.serializedValue; + if (![value isKindOfClass:NSArray.class]) { + // It's a single value, so NSData. + NSData * data = value; + return aEncoder.Encode(PreEncodedValue(AsByteSpan(data))); + } + + // It's a list of data values. + NSArray * dataList = value; + return aEncoder.EncodeList([dataList](const auto & itemEncoder) { + for (NSData * item in dataList) { + ReturnErrorOnFailure(itemEncoder.Encode(PreEncodedValue(AsByteSpan(item)))); + } + return CHIP_NO_ERROR; + }); + } + + // This must be the FeatureMap attribute we synthesized. + if (aPath.mAttributeId == MTRAttributeIDTypeGlobalAttributeFeatureMapID) { + // Feature map defaults to 0. + constexpr uint32_t defaultFeatureMap = 0; + return aEncoder.Encode(defaultFeatureMap); + } + + if (aPath.mAttributeId == MTRAttributeIDTypeGlobalAttributeClusterRevisionID) { + return aEncoder.Encode(mClusterRevision.unsignedLongLongValue); + } + + // Note: This code is not reached for the descriptor cluster, which uses its own AttributeAccessInterface. + + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); +} diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster_Internal.h b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster_Internal.h index 5bd9ba0692ed4a..4ae3d0db348533 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster_Internal.h +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerCluster_Internal.h @@ -21,15 +21,38 @@ #include +// TODO: These attribute-*.h and Span bits are a hack that should eventually go away. +#include +#include + +NS_ASSUME_NONNULL_BEGIN + @interface MTRServerCluster () /** - * Mark this cluster as associated with a particular controller. + * Mark this cluster as associated with a particular controller. The + * controller can be nil to indicate that the endpoint is not associated with a + * specific controller but rather with the controller factory. This method does + * NOT perform any cleanup on failure; it's the caller's responsibility to call + * invalidate if it fails. + */ +- (BOOL)associateWithController:(nullable MTRDeviceController *)controller; + +/** + * Register this cluster. Always called on the Matter queue. + */ +- (void)registerMatterCluster; + +/** + * Unregister this cluster. Always called on the Matter queue. */ -- (BOOL)associateWithController:(MTRDeviceController *)controller; +- (void)unregisterMatterCluster; /** - * Mark this cluster as part of an Defunct-state endpoint. + * Mark this cluster as part of an endpoint that is no longer being used. Can + * run on any thread, but will either be called before registerMatterCluster or + * after unregisterMatterCluster. This undoes anything associateWithController + * did. */ - (void)invalidate; @@ -44,4 +67,31 @@ */ @property (nonatomic, assign) chip::EndpointId parentEndpoint; +/** + * The attribute metadata for the cluster. Only valid after associateWithController: has succeeded. + */ +@property (nonatomic, assign, readonly) chip::Span matterAttributeMetadata; + +/** + * The list of accepted command IDs. + */ +@property (nonatomic, copy, nullable) NSArray * acceptedCommands; + +/** + * The list of generated command IDs. + */ +@property (nonatomic, copy, nullable) NSArray * generatedCommands; + +/** + * The list of accepted commands IDs in the format the Matter stack needs. + */ +@property (nonatomic, assign, nullable, readonly) chip::CommandId * matterAcceptedCommands; + +/** + * The list of generated commands IDs in the format the Matter stack needs. + */ +@property (nonatomic, assign, nullable, readonly) chip::CommandId * matterGeneratedCommands; + @end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.h b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.h index 642dc5fb4ec658..13dbe8454d759e 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.h +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.h @@ -77,8 +77,9 @@ MTR_NEWLY_AVAILABLE /** * A list of server clusters supported on this endpoint. The Descriptor cluster * does not need to be included unless a TagList attribute is desired on it or - * unless it has a non-empty PartsList. If not included, the Descriptor cluster - * will be generated automatically. + * it has a non-empty PartsList, or it needs to have cluster-specific access + * grants. If not included, the Descriptor cluster will be generated + * automatically. */ @property (nonatomic, copy, readonly) NSArray * serverClusters; diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm index ee35e4da0bc0db..5e6df865115be6 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint.mm @@ -19,11 +19,22 @@ #import "MTRLogging_Internal.h" #import "MTRServerCluster_Internal.h" #import "MTRServerEndpoint_Internal.h" +#import #import #include #include #include +#include + +// TODO: These af-types.h and att-storage.h and attribute-storage.h and +// endpoint-config-api.h and probably CodeUtils.h bits are a hack that should +// eventually go away. +#include +#include +#include +#include +#include using namespace chip; @@ -36,6 +47,13 @@ @implementation MTRServerEndpoint { NSMutableSet * _accessGrants; NSMutableArray * _serverClusters; MTRDeviceController * __weak _deviceController; + std::unique_ptr _matterClusterMetadata; + EmberAfEndpointType _matterEndpointMetadata; + std::unique_ptr _matterDeviceTypes; + std::unique_ptr _matterDataVersions; + + // _endpointIndex has a value only when we have the endpoint configured. + std::optional _endpointIndex; } - (nullable instancetype)initWithEndpointID:(NSNumber *)endpointID deviceTypes:(NSArray *)deviceTypes @@ -67,6 +85,11 @@ - (nullable instancetype)initWithEndpointID:(NSNumber *)endpointID deviceTypes:( return [self initInternalWithEndpointID:endpointID deviceTypes:deviceTypes accessGrants:[NSSet set] clusters:@[]]; } ++ (MTRServerEndpoint *)rootNodeEndpoint +{ + return [[MTRServerEndpoint alloc] initInternalWithEndpointID:@(kRootEndpointId) deviceTypes:@[] accessGrants:[NSSet set] clusters:@[]]; +} + - (instancetype)initInternalWithEndpointID:(NSNumber *)endpointID deviceTypes:(NSArray *)deviceTypes accessGrants:(NSSet *)accessGrants clusters:(NSArray *)clusters { if (!(self = [super init])) { @@ -148,7 +171,21 @@ - (BOOL)addServerCluster:(MTRServerCluster *)serverCluster return YES; } -- (BOOL)associateWithController:(MTRDeviceController *)controller +#define MTR_DECLARE_LIST_ATTRIBUTE(attrID) \ + DECLARE_DYNAMIC_ATTRIBUTE(attrID, ARRAY, 0, 0) + +static constexpr EmberAfAttributeMetadata sDescriptorAttributesMetadata[] = { + DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeClusterDescriptorAttributeDeviceTypeListID, ARRAY, 0, 0), + DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeClusterDescriptorAttributeServerListID, ARRAY, 0, 0), + DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeClusterDescriptorAttributeClientListID, ARRAY, 0, 0), + DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeClusterDescriptorAttributePartsListID, ARRAY, 0, 0), + DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeGlobalAttributeFeatureMapID, BITMAP32, 4, 0), + DECLARE_DYNAMIC_ATTRIBUTE(MTRAttributeIDTypeGlobalAttributeClusterRevisionID, INT16U, 2, 0), +}; + +#undef MTR_DECLARE_LIST_ATTRIBUTE + +- (BOOL)associateWithController:(nullable MTRDeviceController *)controller { MTRDeviceController * existingController = _deviceController; if (existingController != nil) { @@ -161,6 +198,17 @@ - (BOOL)associateWithController:(MTRDeviceController *)controller return NO; } + // After this point we have to make sure we clean up on any failures. + if (![self finishAssociationWithController:controller]) { + [self invalidate]; + return NO; + } + + return YES; +} + +- (BOOL)finishAssociationWithController:(nullable MTRDeviceController *)controller +{ for (MTRServerCluster * cluster in _serverClusters) { if (![cluster associateWithController:controller]) { return NO; @@ -170,20 +218,193 @@ - (BOOL)associateWithController:(MTRDeviceController *)controller // Snapshot _matterAccessGrants now; after this point it will only be // updated on the Matter queue. _matterAccessGrants = [_accessGrants copy]; + + // _serverClusters shouldn't be able to change anymore, so we can now + // construct our EmberAfCluster array. + size_t clusterCount = _serverClusters.count; + + // Figure out whether we need to synthesize a Descriptor cluster. + bool needsDescriptor = true; + for (MTRServerCluster * cluster in _serverClusters) { + if ([cluster.clusterID isEqual:@(MTRClusterIDTypeDescriptorID)]) { + needsDescriptor = false; + break; + } + } + + if (needsDescriptor) { + ++clusterCount; + } + + if (clusterCount >= 0xFF) { + // The ember bits don't allow this many clusters (they use 0xFF to mean + // "no such cluster" in various places. + MTR_LOG_ERROR("Unable to create endpoint with %llu clusters; it's too many", + static_cast(clusterCount)); + return NO; + } + + _matterClusterMetadata = std::make_unique(clusterCount); + // std::make_unique never returns null; it will try to throw an exception + // and likely crash on OOM. + + size_t clusterIndex = 0; + for (; clusterIndex < _serverClusters.count; ++clusterIndex) { + auto * cluster = _serverClusters[clusterIndex]; + auto & metadata = _matterClusterMetadata[clusterIndex]; + + metadata.clusterId = static_cast(cluster.clusterID.unsignedLongLongValue); + + auto attrMetadata = cluster.matterAttributeMetadata; + metadata.attributes = attrMetadata.data(); + // This cast is safe because clusters check for this constraint on + // number of attributes. + metadata.attributeCount = static_cast(attrMetadata.size()); + + metadata.clusterSize = 0; // All our attributes are external. + + metadata.mask = CLUSTER_MASK_SERVER; + + metadata.functions = nullptr; // None of our clusters, including Descriptor, uses these. + + metadata.acceptedCommandList = cluster.matterAcceptedCommands; + metadata.generatedCommandList = cluster.matterGeneratedCommands; + + metadata.eventList = nullptr; + metadata.eventCount = 0; + } + + if (needsDescriptor) { + auto & metadata = _matterClusterMetadata[clusterIndex]; + + metadata.clusterId = MTRClusterIDTypeDescriptorID; + + metadata.attributes = sDescriptorAttributesMetadata; + metadata.attributeCount = ArraySize(sDescriptorAttributesMetadata); + + metadata.clusterSize = 0; // All our attributes are external. + + metadata.mask = CLUSTER_MASK_SERVER; + + metadata.functions = nullptr; // Descriptor does not use these. + + metadata.acceptedCommandList = nullptr; + metadata.generatedCommandList = nullptr; + + metadata.eventList = nullptr; + metadata.eventCount = 0; + + ++clusterIndex; + } + + _matterEndpointMetadata.cluster = _matterClusterMetadata.get(); + // Cast is safe, because we did a range check above. + _matterEndpointMetadata.clusterCount = static_cast(clusterCount); + _matterEndpointMetadata.endpointSize = 0; // All our attributes are external. + + _matterDeviceTypes = std::make_unique(_deviceTypes.count); + for (size_t index = 0; index < _deviceTypes.count; ++index) { + auto * deviceType = _deviceTypes[index]; + auto & matterType = _matterDeviceTypes[index]; + + matterType.deviceId = static_cast(deviceType.deviceTypeID.unsignedLongLongValue); + // TODO: The spec allows 16-bit revisions, but the Ember bits only + // support 8-bit.... + matterType.deviceVersion = static_cast(deviceType.deviceTypeRevision.unsignedLongLongValue); + } + + _matterDataVersions = std::make_unique(clusterCount); + _deviceController = controller; return YES; } +- (void)registerMatterEndpoint +{ + assertChipStackLockedByCurrentThread(); + + static_assert(FIXED_ENDPOINT_COUNT == 0, "Indexing will be off"); + + // We can't use emberAfEndpointCount here, because that returns just the + // count of fixed endpoints up until the first call to + // emberAfSetDynamicEndpoint(). + uint16_t possibleEndpointCount = MAX_ENDPOINT_COUNT; + uint16_t index = 0; + for (; index < possibleEndpointCount; ++index) { + if (emberAfEndpointFromIndex(index) == kInvalidEndpointId) { + break; + } + } + + if (index == possibleEndpointCount) { + // Something is very broken. We shouldn't have this many endpoints! + MTR_LOG_ERROR("We somehow ran out of endpoint slots."); + return; + } + + auto status = emberAfSetDynamicEndpoint(index, static_cast(_endpointID.unsignedLongLongValue), + &_matterEndpointMetadata, + Span(_matterDataVersions.get(), _matterEndpointMetadata.clusterCount), + Span(_matterDeviceTypes.get(), _deviceTypes.count)); + if (status != EMBER_ZCL_STATUS_SUCCESS) { + MTR_LOG_ERROR("Unexpected failure to define our Matter endpoint"); + } + + _endpointIndex.emplace(index); + + for (MTRServerCluster * cluster in _serverClusters) { + [cluster registerMatterCluster]; + } +} + +- (void)unregisterMatterEndpoint +{ + assertChipStackLockedByCurrentThread(); + + if (_endpointIndex.has_value()) { + emberAfClearDynamicEndpoint(_endpointIndex.value()); + _endpointIndex.reset(); + } + + for (MTRServerCluster * cluster in _serverClusters) { + [cluster unregisterMatterCluster]; + } +} + - (void)invalidate { + // Undo any work associateWithController did. for (MTRServerCluster * cluster in _serverClusters) { [cluster invalidate]; } + // We generally promise to only touch _matterAccessGrants on the Matter + // queue after associateWithController succeeds, but we are no longer being + // looked at from that queue, so it's safe to reset it here. + _matterAccessGrants = [NSSet set]; + _matterEndpointMetadata.cluster = nullptr; + _matterEndpointMetadata.clusterCount = 0; + _matterClusterMetadata.reset(); + _matterDeviceTypes.reset(); + _matterDataVersions.reset(); _deviceController = nil; } +- (NSArray *)matterAccessGrantsForCluster:(NSNumber *)clusterID +{ + assertChipStackLockedByCurrentThread(); + + NSMutableArray * grants = [[_matterAccessGrants allObjects] mutableCopy]; + for (MTRServerCluster * cluster in _serverClusters) { + if ([cluster.clusterID isEqual:clusterID]) { + [grants addObjectsFromArray:[cluster.matterAccessGrants allObjects]]; + } + } + + return [grants copy]; +} + - (NSArray *)accessGrants { return [_accessGrants allObjects]; diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint_Internal.h b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint_Internal.h index 97725c2b1afad5..1ca4d4da7533a5 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint_Internal.h +++ b/src/darwin/Framework/CHIP/ServerEndpoint/MTRServerEndpoint_Internal.h @@ -18,18 +18,49 @@ #import #import +NS_ASSUME_NONNULL_BEGIN + @interface MTRServerEndpoint () /** - * Mark this endpoint as associated with a particular controller. + * Mark this endpoint as associated with a particular controller. The + * controller can be nil to indicate that the endpoint is not associated with a + * specific controller but rather with the controller factory. + * + * On failure, this method ensures that it undoes any state changes it made. + */ +- (BOOL)associateWithController:(nullable MTRDeviceController *)controller; + +/** + * Register this endpoint. Always called on the Matter queue. + */ +- (void)registerMatterEndpoint; + +/** + * Unregister this endpoint. Always called on the Matter queue. */ -- (BOOL)associateWithController:(MTRDeviceController *)controller; +- (void)unregisterMatterEndpoint; /** - * Mark this endpoint as being in a Defunct state. + * Mark this endpoint as no longer being in use. Can run on any thread, but + * will either be called before registerMatterEndpoint or after + * unregisterMatterEndpoint. This undoes anything associateWithController did. */ - (void)invalidate; +/** + * Get an MTRServerEndpoint for the root node endpoint. This can't be done via + * the public initializer, since we don't allow that to create an + * MTRServerEndpoint for endpoint 0. + */ ++ (MTRServerEndpoint *)rootNodeEndpoint; + +/** + * Returns the list of access grants applicable to the given cluster ID on this + * endpoint. Only called on the Matter queue. + */ +- (NSArray *)matterAccessGrantsForCluster:(NSNumber *)clusterID; + /** * The access grants the Matter stack can observe. Only modified while in * Initializing state or on the Matter queue. @@ -37,3 +68,5 @@ @property (nonatomic, strong, readonly) NSSet * matterAccessGrants; @end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/app/PluginApplicationCallbacks.h b/src/darwin/Framework/CHIP/app/PluginApplicationCallbacks.h new file mode 100644 index 00000000000000..f21f1e15302186 --- /dev/null +++ b/src/darwin/Framework/CHIP/app/PluginApplicationCallbacks.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 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. + */ + +/** + * This file is only here to satisfy includes in core files that expect an + * app/PluginApplicationCallbacks.h. This file is NOT generated by ZAP, and + * provides the bare minimum information needed to allow things to compile. + * + * TODO: This needs a better setup. + */ + +void MatterDescriptorPluginServerInitCallback(); + +#define MATTER_PLUGINS_INIT MatterDescriptorPluginServerInitCallback(); diff --git a/src/darwin/Framework/CHIP/zap-generated/endpoint_config.h b/src/darwin/Framework/CHIP/zap-generated/endpoint_config.h new file mode 100644 index 00000000000000..27cfbdb778d5f5 --- /dev/null +++ b/src/darwin/Framework/CHIP/zap-generated/endpoint_config.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 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. + */ + +/** + * This file is only here to satisfy includes in core files that expect an + * endpoint config. This file is NOT generated by ZAP, and provides the bare + * minimum information needed to allow things to compile. + * + * TO DO: This needs a better setup. + */ +#include + +/** + * We don't have any fixed endpoints. + */ +#define FIXED_ENDPOINT_COUNT 0 + +/** + * We don't have any attributes not implemented by AttributeAccessInterface. But + * using 0 here does not work, so just claim 1. + */ +#define ATTRIBUTE_LARGEST 1 + +#define GENERATED_ATTRIBUTES {} + +#define GENERATED_ENDPOINT_TYPES {} + +#define FIXED_DEVICE_TYPES {} + +#define ZAP_FIXED_ENDPOINT_DATA_VERSION_COUNT 0 + +#define FIXED_ENDPOINT_ARRAY {} + +#define FIXED_DEVICE_TYPE_LENGTHS {} + +#define FIXED_DEVICE_TYPE_OFFSETS {} + +#define FIXED_ENDPOINT_TYPES {} diff --git a/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m b/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m index 8bbb529236671e..effce2ef5b3403 100644 --- a/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m +++ b/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m @@ -32,6 +32,12 @@ static NSString * kOnboardingPayload = @"MT:-24J0AFN00KA0648G00"; static const uint16_t kTestVendorId = 0xFFF1u; +#ifdef DEBUG +@interface MTRDeviceController (Test) ++ (void)forceLocalhostAdvertisingOnly; +@end +#endif // DEBUG + @interface MTRPerControllerStorageTestsControllerDelegate : NSObject @property (nonatomic, strong) XCTestExpectation * expectation; @property (nonatomic, strong) NSNumber * deviceID; @@ -270,6 +276,9 @@ - (nullable MTRDeviceController *)startControllerWithRootKeys:(MTRTestKeys *)roo intermediateCertificate:nil rootCertificate:root]; XCTAssertNotNil(params); + // TODO: This is only used by testControllerServer. If that moves + // elsewhere, take this back out again. + params.shouldAdvertiseOperational = YES; __auto_type * ourCertificateIssuer = [[MTRPerControllerStorageTestsCertificateIssuer alloc] initWithRootCertificate:root intermediateCertificate:nil @@ -1035,6 +1044,471 @@ - (void)test007_TestMultipleControllers XCTAssertFalse([controller3 isRunning]); } +// TODO: This might want to go in a separate test file, with some shared setup +// across multiple tests, maybe. Would need to factor out +// startControllerWithRootKeys into a test helper. +- (void)testControllerServer +{ +#ifdef DEBUG + // Force our controllers to only advertise on localhost, to avoid DNS-SD + // crosstalk. + [MTRDeviceController forceLocalhostAdvertisingOnly]; +#endif // DEBUG + + __auto_type queue = dispatch_get_main_queue(); + + __auto_type * rootKeys = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(rootKeys); + + NSNumber * fabricID = @(456); + + __auto_type * operationalKeysServer = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(operationalKeysServer); + + __auto_type * storageDelegateServer = [[MTRTestPerControllerStorage alloc] initWithControllerID:[NSUUID UUID]]; + XCTAssertNotNil(storageDelegateServer); + + NSNumber * nodeIDServer = @(123); + + NSError * error; + MTRDeviceController * controllerServer = [self startControllerWithRootKeys:rootKeys + operationalKeys:operationalKeysServer + fabricID:fabricID + nodeID:nodeIDServer + storage:storageDelegateServer + error:&error]; + XCTAssertNil(error); + XCTAssertNotNil(controllerServer); + XCTAssertTrue([controllerServer isRunning]); + XCTAssertEqualObjects(controllerServer.controllerNodeID, nodeIDServer); + + __auto_type * endpointId1 = @(10); + __auto_type * endpointId2 = @(20); + __auto_type * endpointId3 = @(30); + __auto_type * clusterId1 = @(0xFFF1FC02); + __auto_type * clusterId2 = @(0xFFF1FC10); + __auto_type * clusterRevision1 = @(3); + __auto_type * clusterRevision2 = @(4); + __auto_type * attributeId1 = @(0); + __auto_type * attributeId2 = @(0xFFF10002); + + __auto_type * unsignedIntValue1 = @{ + MTRTypeKey : MTRUnsignedIntegerValueType, + MTRValueKey : @(5), + }; + + __auto_type * unsignedIntValue2 = @{ + MTRTypeKey : MTRUnsignedIntegerValueType, + MTRValueKey : @(7), + }; + + __auto_type * structValue1 = @{ + MTRTypeKey : MTRStructureValueType, + MTRValueKey : @[ + @{ + MTRContextTagKey : @(1), + MTRDataKey : @ { + MTRTypeKey : MTRUnsignedIntegerValueType, + MTRValueKey : @(1), + }, + }, + @{ + MTRContextTagKey : @(2), + MTRDataKey : @ { + MTRTypeKey : MTRUTF8StringValueType, + MTRValueKey : @"struct1", + }, + }, + ], + }; + + __auto_type * structValue2 = @{ + MTRTypeKey : MTRStructureValueType, + MTRValueKey : @[ + @{ + MTRContextTagKey : @(1), + MTRDataKey : @ { + MTRTypeKey : MTRUnsignedIntegerValueType, + MTRValueKey : @(2), + }, + }, + @{ + MTRContextTagKey : @(2), + MTRDataKey : @ { + MTRTypeKey : MTRUTF8StringValueType, + MTRValueKey : @"struct2", + }, + }, + ], + }; + + __auto_type * listOfStructsValue1 = @{ + MTRTypeKey : MTRArrayValueType, + MTRValueKey : @[ + @{ + MTRDataKey : structValue1, + }, + @{ + MTRDataKey : structValue2, + }, + ], + }; + +#if 0 + __auto_type * listOfStructsValue2 = @{ + MTRTypeKey : MTRArrayValueType, + MTRValueKey : @[ + @{ MTRDataKey: structValue2, }, + ], + }; +#endif + + __auto_type responsePathFromRequestPath = ^(MTRAttributeRequestPath * path) { + return [MTRAttributePath attributePathWithEndpointID:path.endpoint clusterID:path.cluster attributeID:path.attribute]; + }; + + // Set up an endpoint on the server. + __auto_type * deviceType1 = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(0xFFF10001) revision:@(1)]; + XCTAssertNotNil(deviceType1); + + __auto_type * endpoint1 = [[MTRServerEndpoint alloc] initWithEndpointID:endpointId1 deviceTypes:@[ deviceType1 ]]; + XCTAssertNotNil(endpoint1); + + __auto_type * cluster1 = [[MTRServerCluster alloc] initWithClusterID:clusterId1 revision:clusterRevision1]; + XCTAssertNotNil(cluster1); + + __auto_type * cluster2 = [[MTRServerCluster alloc] initWithClusterID:clusterId2 revision:clusterRevision2]; + XCTAssertNotNil(cluster1); + + __auto_type * attribute1 = [[MTRServerAttribute alloc] initReadonlyAttributeWithID:attributeId1 initialValue:unsignedIntValue1 requiredPrivilege:MTRAccessControlEntryPrivilegeView]; + XCTAssertNotNil(attribute1); + __auto_type * attribute1RequestPath = [MTRAttributeRequestPath requestPathWithEndpointID:endpointId1 + clusterID:clusterId1 + attributeID:attributeId1]; + XCTAssertNotNil(attribute1RequestPath); + __auto_type * attribute1ResponsePath = responsePathFromRequestPath(attribute1RequestPath); + XCTAssertNotNil(attribute1ResponsePath); + + __auto_type * attribute2 = [[MTRServerAttribute alloc] initReadonlyAttributeWithID:attributeId2 initialValue:listOfStructsValue1 requiredPrivilege:MTRAccessControlEntryPrivilegeManage]; + XCTAssertNotNil(attribute2); + __auto_type * attribute2RequestPath = [MTRAttributeRequestPath requestPathWithEndpointID:endpointId1 + clusterID:clusterId2 + attributeID:attributeId2]; + XCTAssertNotNil(attribute2RequestPath); + __auto_type * attribute2ResponsePath = responsePathFromRequestPath(attribute2RequestPath); + XCTAssertNotNil(attribute2ResponsePath); + + __auto_type * attribute3 = [[MTRServerAttribute alloc] initReadonlyAttributeWithID:attributeId2 initialValue:unsignedIntValue1 requiredPrivilege:MTRAccessControlEntryPrivilegeOperate]; + XCTAssertNotNil(attribute3); + __auto_type * attribute3RequestPath = [MTRAttributeRequestPath requestPathWithEndpointID:endpointId1 + clusterID:clusterId1 + attributeID:attributeId2]; + XCTAssertNotNil(attribute3RequestPath); + __auto_type * attribute3ResponsePath = responsePathFromRequestPath(attribute3RequestPath); + XCTAssertNotNil(attribute3ResponsePath); + + XCTAssertTrue([cluster1 addAttribute:attribute1]); + XCTAssertTrue([cluster1 addAttribute:attribute3]); + + XCTAssertTrue([cluster2 addAttribute:attribute2]); + + XCTAssertTrue([endpoint1 addServerCluster:cluster1]); + XCTAssertTrue([endpoint1 addServerCluster:cluster2]); + + [endpoint1 addAccessGrant:[MTRAccessGrant accessGrantForAllNodesWithPrivilege:MTRAccessControlEntryPrivilegeView]]; + + XCTAssertTrue([controllerServer addServerEndpoint:endpoint1]); + + __auto_type * endpoint2 = [[MTRServerEndpoint alloc] initWithEndpointID:endpointId2 deviceTypes:@[ deviceType1 ]]; + XCTAssertNotNil(endpoint2); + // Should be able to add this endpoint as well. + XCTAssertTrue([controllerServer addServerEndpoint:endpoint2]); + + __auto_type * endpoint3 = [[MTRServerEndpoint alloc] initWithEndpointID:endpointId2 deviceTypes:@[ deviceType1 ]]; + XCTAssertNotNil(endpoint3); + // Should not be able to add this endpoint, since it's got a duplicate + // endpoint id. + XCTAssertFalse([controllerServer addServerEndpoint:endpoint3]); + + __auto_type * operationalKeysClient = [[MTRTestKeys alloc] init]; + XCTAssertNotNil(operationalKeysClient); + + __auto_type * storageDelegateClient = [[MTRTestPerControllerStorage alloc] initWithControllerID:[NSUUID UUID]]; + XCTAssertNotNil(storageDelegateClient); + + NSNumber * nodeIDClient = @(789); + + MTRDeviceController * controllerClient = [self startControllerWithRootKeys:rootKeys + operationalKeys:operationalKeysClient + fabricID:fabricID + nodeID:nodeIDClient + storage:storageDelegateClient + error:&error]; + XCTAssertNil(error); + XCTAssertNotNil(controllerClient); + XCTAssertTrue([controllerClient isRunning]); + XCTAssertEqualObjects(controllerClient.controllerNodeID, nodeIDClient); + + __auto_type * endpoint4 = [[MTRServerEndpoint alloc] initWithEndpointID:endpointId2 deviceTypes:@[ deviceType1 ]]; + XCTAssertNotNil(endpoint4); + // Should not be able to add this endpoint, since it's got a duplicate + // endpoint id, even though we are adding on a different controller. + XCTAssertFalse([controllerClient addServerEndpoint:endpoint4]); + + __auto_type * endpoint5 = [[MTRServerEndpoint alloc] initWithEndpointID:endpointId3 deviceTypes:@[ deviceType1 ]]; + XCTAssertNotNil(endpoint5); + // Should be able to add this one, though; it's unrelated to any existing endpoints. + XCTAssertTrue([controllerClient addServerEndpoint:endpoint5]); + + __auto_type * device = [MTRBaseDevice deviceWithNodeID:nodeIDServer controller:controllerClient]; + + __auto_type * requestPath = attribute1RequestPath; + __block __auto_type * responsePath = attribute1ResponsePath; + + __auto_type checkSingleValue = ^(NSArray *> * _Nullable values, NSError * _Nullable error, NSDictionary * expectedValue) { + // The overall interaction should succeed. + XCTAssertNil(error); + XCTAssertNotNil(values); + + // And we should get a value for our attribute. + XCTAssertEqual(values.count, 1); + + NSDictionary * value = values[0]; + XCTAssertEqualObjects(value[MTRAttributePathKey], responsePath); + + XCTAssertNil(value[MTRErrorKey]); + XCTAssertNotNil(value[MTRDataKey]); + + XCTAssertEqualObjects(value[MTRDataKey], expectedValue); + }; + + __auto_type checkSinglePathError = ^(NSArray *> * _Nullable values, NSError * _Nullable error, MTRInteractionErrorCode expectedError) { + // The overall interaction should succeed. + XCTAssertNil(error); + XCTAssertNotNil(values); + + // And we should get a value for our attribute. + XCTAssertEqual(values.count, 1); + + NSDictionary * value = values[0]; + XCTAssertEqualObjects(value[MTRAttributePathKey], responsePath); + + XCTAssertNil(value[MTRDataKey]); + XCTAssertNotNil(value[MTRErrorKey]); + + NSError * pathError = value[MTRErrorKey]; + XCTAssertEqual(pathError.domain, MTRInteractionErrorDomain); + XCTAssertEqual(pathError.code, expectedError); + }; + + // First try a basic read. + XCTestExpectation * readExpectation1 = [self expectationWithDescription:@"Read 1 of attribute complete"]; + [device readAttributePaths:@[ requestPath ] + eventPaths:nil + params:nil + queue:queue + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + checkSingleValue(values, error, unsignedIntValue1); + [readExpectation1 fulfill]; + }]; + [self waitForExpectations:@[ readExpectation1 ] timeout:kTimeoutInSeconds]; + + // Now try a basic subscribe. + __block void (^reportHandler)(NSArray *> * _Nullable values, NSError * _Nullable error); + + XCTestExpectation * initialValueExpectation = [self expectationWithDescription:@"Got initial value"]; + reportHandler = ^(NSArray *> * _Nullable values, NSError * _Nullable error) { + checkSingleValue(values, error, unsignedIntValue1); + [initialValueExpectation fulfill]; + }; + + XCTestExpectation * subscriptionEstablishedExpectation = [self expectationWithDescription:@"Basic subscription established"]; + __auto_type * subscribeParams = [[MTRSubscribeParams alloc] initWithMinInterval:@(0) maxInterval:@(10)]; + [device subscribeToAttributesWithEndpointID:requestPath.endpoint clusterID:requestPath.cluster attributeID:requestPath.attribute + params:subscribeParams + queue:queue + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + reportHandler(values, error); + } + subscriptionEstablished:^() { + [subscriptionEstablishedExpectation fulfill]; + }]; + [self waitForExpectations:@[ subscriptionEstablishedExpectation, initialValueExpectation ] timeout:kTimeoutInSeconds]; + + // Now change the value and expect to see it on our subscription. + XCTestExpectation * valueUpdateExpectation = [self expectationWithDescription:@"We see the new value"]; + reportHandler = ^(NSArray *> * _Nullable values, NSError * _Nullable error) { + checkSingleValue(values, error, unsignedIntValue2); + [valueUpdateExpectation fulfill]; + }; + + [attribute1 setValue:unsignedIntValue2]; + + [self waitForExpectations:@[ valueUpdateExpectation ] timeout:kTimeoutInSeconds]; + + // Now try a read of an attribute we do not have permissions for. + requestPath = attribute2RequestPath; + responsePath = attribute2ResponsePath; + XCTestExpectation * readNoPermissionsExpectation1 = [self expectationWithDescription:@"Read 1 of attribute with no permissions complete"]; + [device readAttributePaths:@[ requestPath ] + eventPaths:nil + params:nil + queue:queue + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + checkSinglePathError(values, error, MTRInteractionErrorCodeUnsupportedAccess); + [readNoPermissionsExpectation1 fulfill]; + }]; + [self waitForExpectations:@[ readNoPermissionsExpectation1 ] timeout:kTimeoutInSeconds]; + + // Change the permissions to give Manage access on the cluster to some + // random node ID and try again. Should still have no permissions. + __auto_type * unrelatedGrant = [MTRAccessGrant accessGrantForNodeID:@(0xabc) privilege:MTRAccessControlEntryPrivilegeManage]; + XCTAssertNotNil(unrelatedGrant); + [cluster2 addAccessGrant:unrelatedGrant]; + + XCTestExpectation * readNoPermissionsExpectation2 = [self expectationWithDescription:@"Read 2 of attribute with no permissions complete"]; + [device readAttributePaths:@[ requestPath ] + eventPaths:nil + params:nil + queue:queue + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + checkSinglePathError(values, error, MTRInteractionErrorCodeUnsupportedAccess); + [readNoPermissionsExpectation2 fulfill]; + }]; + [self waitForExpectations:@[ readNoPermissionsExpectation2 ] timeout:kTimeoutInSeconds]; + + // Change the permissions to give Manage access on the cluster to our client + // node ID and try again. Should be able to read the attribute now. + __auto_type * clientManageGrant = [MTRAccessGrant accessGrantForNodeID:nodeIDClient privilege:MTRAccessControlEntryPrivilegeManage]; + XCTAssertNotNil(clientManageGrant); + [cluster2 addAccessGrant:clientManageGrant]; + + XCTestExpectation * readExpectation2 = [self expectationWithDescription:@"Read 2 of attribute complete"]; + [device readAttributePaths:@[ requestPath ] + eventPaths:nil + params:nil + queue:queue + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + checkSingleValue(values, error, listOfStructsValue1); + [readExpectation2 fulfill]; + }]; + [self waitForExpectations:@[ readExpectation2 ] timeout:kTimeoutInSeconds]; + + // Adding Manage permissions to one cluster should not affect another one. + requestPath = attribute3RequestPath; + responsePath = attribute3ResponsePath; + + XCTestExpectation * readNoPermissionsExpectation3 = [self expectationWithDescription:@"Read 3 of attribute with no permissions complete"]; + [device readAttributePaths:@[ requestPath ] + eventPaths:nil + params:nil + queue:queue + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + checkSinglePathError(values, error, MTRInteractionErrorCodeUnsupportedAccess); + [readNoPermissionsExpectation3 fulfill]; + }]; + [self waitForExpectations:@[ readNoPermissionsExpectation3 ] timeout:kTimeoutInSeconds]; + + // But adding Manage permissions on the endpoint should grant Operate on + // the cluster. + [endpoint1 addAccessGrant:clientManageGrant]; + + XCTestExpectation * readExpectation3 = [self expectationWithDescription:@"Read 3 of attribute complete"]; + [device readAttributePaths:@[ requestPath ] + eventPaths:nil + params:nil + queue:queue + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + checkSingleValue(values, error, unsignedIntValue1); + [readExpectation3 fulfill]; + }]; + [self waitForExpectations:@[ readExpectation3 ] timeout:kTimeoutInSeconds]; + + // And removing that grant should remove the permissions again. + [endpoint1 removeAccessGrant:clientManageGrant]; + + XCTestExpectation * readNoPermissionsExpectation4 = [self expectationWithDescription:@"Read 4 of attribute with no permissions complete"]; + [device readAttributePaths:@[ requestPath ] + eventPaths:nil + params:nil + queue:queue + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + checkSinglePathError(values, error, MTRInteractionErrorCodeUnsupportedAccess); + [readNoPermissionsExpectation4 fulfill]; + }]; + [self waitForExpectations:@[ readNoPermissionsExpectation4 ] timeout:kTimeoutInSeconds]; + + // Now do a wildcard read on the endpoint and check that this does the right + // thing (gets the right things from descriptor, gets both clusters, etc). +#if 0 + // Unused bits ifdefed out until we doing more testing on the actual values + // we get back. + __auto_type globalAttributePath = ^(NSNumber * clusterID, MTRAttributeIDType attributeID) { + return [MTRAttributePath attributePathWithEndpointID:endpointId1 clusterID:clusterID attributeID:@(attributeID)]; + }; + __auto_type unsignedIntValue = ^(NSUInteger value) { + return @{ + MTRTypeKey: MTRUnsignedIntegerValueType, + MTRValueKey: @(value), + }; + }; + __auto_type arrayOfUnsignedIntegersValue = ^(NSArray * values) { + __auto_type * mutableArray = [[NSMutableArray alloc] init]; + for (NSNumber * value in values) { + [mutableArray addObject:@{ MTRDataKey: @{ + MTRTypeKey: MTRUnsignedIntegerValueType, + MTRValueKey: value, + }, }]; + } + return @{ + MTRTypeKey: MTRArrayValueType, + MTRValueKey: [mutableArray copy], + }; + }; +#endif + XCTestExpectation * wildcardReadExpectation = [self expectationWithDescription:@"Wildcard read of our endpoint"]; + [device readAttributePaths:@[ [MTRAttributeRequestPath requestPathWithEndpointID:endpointId1 clusterID:nil attributeID:nil] ] + eventPaths:nil + params:nil + queue:queue + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + XCTAssertNil(error); + XCTAssertNotNil(values); + + // TODO: Figure out how to test that values is correct that's not + // too fragile if things get returned in different valid order. + // For now just check that every path we got has a value, not an + // error. + for (NSDictionary * value in values) { + XCTAssertNotNil(value[MTRAttributePathKey]); + XCTAssertNil(value[MTRErrorKey]); + XCTAssertNotNil(value[MTRDataKey]); + } +#if 0 + XCTAssertEqualObjects(values, @[ + // cluster1 + @{ MTRAttributePathKey: attribute1ResponsePath, + MTRDataKey: unsignedIntValue2, }, + @{ MTRAttributePathKey: globalAttributePath(clusterId1, MTRAttributeIDTypeGlobalAttributeFeatureMapID), + MTRDataKey: unsignedIntValue(0), }, + @{ MTRAttributePathKey: globalAttributePath(clusterId1, MTRAttributeIDTypeGlobalAttributeClusterRevisionID), + MTRDataKey: clusterRevision1, }, + @{ MTRAttributePathKey: globalAttributePath(clusterId1, MTRAttributeIDTypeGlobalAttributeGeneratedCommandListID), + MTRDataKey: arrayOfUnsignedIntegersValue(@[]), }, + @{ MTRAttributePathKey: globalAttributePath(clusterId1, MTRAttributeIDTypeGlobalAttributeAcceptedCommandListID), + MTRDataKey: arrayOfUnsignedIntegersValue(@[]), }, + // etc + + ]); +#endif + [wildcardReadExpectation fulfill]; + }]; + [self waitForExpectations:@[ wildcardReadExpectation ] timeout:kTimeoutInSeconds]; + + [controllerClient shutdown]; + [controllerServer shutdown]; +} + @end #endif // MTR_PER_CONTROLLER_STORAGE_ENABLED diff --git a/src/darwin/Framework/CHIPTests/MTRServerEndpointTests.m b/src/darwin/Framework/CHIPTests/MTRServerEndpointTests.m index 57b47f8eb540b7..4a7c31a25a8b02 100644 --- a/src/darwin/Framework/CHIPTests/MTRServerEndpointTests.m +++ b/src/darwin/Framework/CHIPTests/MTRServerEndpointTests.m @@ -130,12 +130,16 @@ - (void)testServerAttribute MTRTypeKey : MTRArrayValueType, MTRValueKey : @[ @{ - MTRTypeKey : MTRUTF8StringValueType, - MTRValueKey : @"str1", + MTRDataKey : @ { + MTRTypeKey : MTRUTF8StringValueType, + MTRValueKey : @"str1", + }, }, @{ - MTRTypeKey : MTRUTF8StringValueType, - MTRValueKey : @"str2", + MTRDataKey : @ { + MTRTypeKey : MTRUTF8StringValueType, + MTRValueKey : @"str2", + }, }, ], }; diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index 5c735e19329ad8..552a797d34a08f 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -150,6 +150,18 @@ 5143851E2A65885500EDC8E6 /* MTRSwiftPairingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5143851D2A65885500EDC8E6 /* MTRSwiftPairingTests.swift */; }; 514654492A72F9DF00904E61 /* MTRDemuxingStorage.mm in Sources */ = {isa = PBXBuildFile; fileRef = 514654482A72F9DF00904E61 /* MTRDemuxingStorage.mm */; }; 5146544B2A72F9F500904E61 /* MTRDemuxingStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 5146544A2A72F9F500904E61 /* MTRDemuxingStorage.h */; }; + 514C79ED2B62ADCD00DD6D7B /* ember-compatibility-functions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79EC2B62ADCD00DD6D7B /* ember-compatibility-functions.cpp */; }; + 514C79EE2B62ADCD00DD6D7B /* ember-compatibility-functions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79EC2B62ADCD00DD6D7B /* ember-compatibility-functions.cpp */; }; + 514C79F02B62ADDA00DD6D7B /* descriptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79EF2B62ADDA00DD6D7B /* descriptor.cpp */; }; + 514C79F12B62ADDA00DD6D7B /* descriptor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79EF2B62ADDA00DD6D7B /* descriptor.cpp */; }; + 514C79F32B62ED5500DD6D7B /* attribute-storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79F22B62ED5500DD6D7B /* attribute-storage.cpp */; }; + 514C79F42B62ED5500DD6D7B /* attribute-storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79F22B62ED5500DD6D7B /* attribute-storage.cpp */; }; + 514C79F62B62F0B900DD6D7B /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79F52B62F0B900DD6D7B /* util.cpp */; }; + 514C79F72B62F0B900DD6D7B /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79F52B62F0B900DD6D7B /* util.cpp */; }; + 514C79F92B62F60100DD6D7B /* message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79F82B62F60100DD6D7B /* message.cpp */; }; + 514C79FA2B62F60100DD6D7B /* message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79F82B62F60100DD6D7B /* message.cpp */; }; + 514C79FC2B62F94C00DD6D7B /* ota-provider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79FB2B62F94C00DD6D7B /* ota-provider.cpp */; }; + 514C79FD2B62F94C00DD6D7B /* ota-provider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 514C79FB2B62F94C00DD6D7B /* ota-provider.cpp */; }; 514C7A012B64223400DD6D7B /* MTRServerAttribute_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 514C79FF2B64223400DD6D7B /* MTRServerAttribute_Internal.h */; }; 514C7A022B64223400DD6D7B /* MTRServerEndpoint_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 514C7A002B64223400DD6D7B /* MTRServerEndpoint_Internal.h */; }; 514C7A042B6436D500DD6D7B /* MTRServerCluster_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 514C7A032B6436D500DD6D7B /* MTRServerCluster_Internal.h */; }; @@ -160,6 +172,13 @@ 51565CB62A7B0D6600469F18 /* MTRDeviceControllerParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 51565CB52A7B0D6600469F18 /* MTRDeviceControllerParameters.h */; settings = {ATTRIBUTES = (Public, ); }; }; 515C1C6F284F9FFB00A48F0C /* MTRFramework.mm in Sources */ = {isa = PBXBuildFile; fileRef = 515C1C6D284F9FFB00A48F0C /* MTRFramework.mm */; }; 515C1C70284F9FFB00A48F0C /* MTRFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 515C1C6E284F9FFB00A48F0C /* MTRFramework.h */; }; + 516411312B6BF70300E67C05 /* DataModelHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 516415FE2B6B132200D5CE11 /* DataModelHandler.cpp */; }; + 516411322B6BF75700E67C05 /* MTRIMDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = 516416002B6B483C00D5CE11 /* MTRIMDispatch.mm */; }; + 516411332B6BF77700E67C05 /* MTRServerAccessControl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 516415FA2B6ACA8300D5CE11 /* MTRServerAccessControl.mm */; }; + 516415FC2B6ACA8300D5CE11 /* MTRServerAccessControl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 516415FA2B6ACA8300D5CE11 /* MTRServerAccessControl.mm */; }; + 516415FD2B6ACA8300D5CE11 /* MTRServerAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 516415FB2B6ACA8300D5CE11 /* MTRServerAccessControl.h */; }; + 516415FF2B6B132200D5CE11 /* DataModelHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 516415FE2B6B132200D5CE11 /* DataModelHandler.cpp */; }; + 516416012B6B483C00D5CE11 /* MTRIMDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = 516416002B6B483C00D5CE11 /* MTRIMDispatch.mm */; }; 51669AF02913204400F4AA36 /* MTRBackwardsCompatTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 51669AEF2913204400F4AA36 /* MTRBackwardsCompatTests.m */; }; 5173A47529C0E2ED00F67F48 /* MTRFabricInfo_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5173A47229C0E2ED00F67F48 /* MTRFabricInfo_Internal.h */; }; 5173A47629C0E2ED00F67F48 /* MTRFabricInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5173A47329C0E2ED00F67F48 /* MTRFabricInfo.mm */; }; @@ -183,8 +202,6 @@ 51B22C2A2740CB47008D5055 /* MTRCommandPayloadsObjc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51B22C292740CB47008D5055 /* MTRCommandPayloadsObjc.mm */; }; 51C8E3F82825CDB600D47D00 /* MTRTestKeys.m in Sources */ = {isa = PBXBuildFile; fileRef = 51C8E3F72825CDB600D47D00 /* MTRTestKeys.m */; }; 51C984622A61CE2A00B0AD9A /* MTRFabricInfoChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 51C984602A61CE2A00B0AD9A /* MTRFabricInfoChecker.m */; }; - 51CFDDB12AC5F78F00DA7CA5 /* EmptyDataModelHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51CFDDB02AC5F78F00DA7CA5 /* EmptyDataModelHandler.cpp */; }; - 51CFDDB22AC5F78F00DA7CA5 /* EmptyDataModelHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51CFDDB02AC5F78F00DA7CA5 /* EmptyDataModelHandler.cpp */; }; 51D0B1272B617246006E3511 /* MTRServerEndpoint.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51D0B1252B617246006E3511 /* MTRServerEndpoint.mm */; }; 51D0B1282B617246006E3511 /* MTRServerEndpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 51D0B1262B617246006E3511 /* MTRServerEndpoint.h */; settings = {ATTRIBUTES = (Public, ); }; }; 51D0B12A2B61766F006E3511 /* MTRServerEndpointTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 51D0B1292B61766F006E3511 /* MTRServerEndpointTests.m */; }; @@ -535,6 +552,12 @@ 5143851D2A65885500EDC8E6 /* MTRSwiftPairingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MTRSwiftPairingTests.swift; sourceTree = ""; }; 514654482A72F9DF00904E61 /* MTRDemuxingStorage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRDemuxingStorage.mm; sourceTree = ""; }; 5146544A2A72F9F500904E61 /* MTRDemuxingStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDemuxingStorage.h; sourceTree = ""; }; + 514C79EC2B62ADCD00DD6D7B /* ember-compatibility-functions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "ember-compatibility-functions.cpp"; path = "util/ember-compatibility-functions.cpp"; sourceTree = ""; }; + 514C79EF2B62ADDA00DD6D7B /* descriptor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = descriptor.cpp; path = clusters/descriptor/descriptor.cpp; sourceTree = ""; }; + 514C79F22B62ED5500DD6D7B /* attribute-storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "attribute-storage.cpp"; path = "util/attribute-storage.cpp"; sourceTree = ""; }; + 514C79F52B62F0B900DD6D7B /* util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = util.cpp; path = util/util.cpp; sourceTree = ""; }; + 514C79F82B62F60100DD6D7B /* message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = message.cpp; path = util/message.cpp; sourceTree = ""; }; + 514C79FB2B62F94C00DD6D7B /* ota-provider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "ota-provider.cpp"; path = "clusters/ota-provider/ota-provider.cpp"; sourceTree = ""; }; 514C79FF2B64223400DD6D7B /* MTRServerAttribute_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRServerAttribute_Internal.h; sourceTree = ""; }; 514C7A002B64223400DD6D7B /* MTRServerEndpoint_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRServerEndpoint_Internal.h; sourceTree = ""; }; 514C7A032B6436D500DD6D7B /* MTRServerCluster_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRServerCluster_Internal.h; sourceTree = ""; }; @@ -545,6 +568,10 @@ 51565CB52A7B0D6600469F18 /* MTRDeviceControllerParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDeviceControllerParameters.h; sourceTree = ""; }; 515C1C6D284F9FFB00A48F0C /* MTRFramework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRFramework.mm; sourceTree = ""; }; 515C1C6E284F9FFB00A48F0C /* MTRFramework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRFramework.h; sourceTree = ""; }; + 516415FA2B6ACA8300D5CE11 /* MTRServerAccessControl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRServerAccessControl.mm; sourceTree = ""; }; + 516415FB2B6ACA8300D5CE11 /* MTRServerAccessControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRServerAccessControl.h; sourceTree = ""; }; + 516415FE2B6B132200D5CE11 /* DataModelHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DataModelHandler.cpp; path = util/DataModelHandler.cpp; sourceTree = ""; }; + 516416002B6B483C00D5CE11 /* MTRIMDispatch.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRIMDispatch.mm; sourceTree = ""; }; 51669AEF2913204400F4AA36 /* MTRBackwardsCompatTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRBackwardsCompatTests.m; sourceTree = ""; }; 5173A47229C0E2ED00F67F48 /* MTRFabricInfo_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRFabricInfo_Internal.h; sourceTree = ""; }; 5173A47329C0E2ED00F67F48 /* MTRFabricInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRFabricInfo.mm; sourceTree = ""; }; @@ -575,7 +602,6 @@ 51C8E3F72825CDB600D47D00 /* MTRTestKeys.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRTestKeys.m; sourceTree = ""; }; 51C984602A61CE2A00B0AD9A /* MTRFabricInfoChecker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRFabricInfoChecker.m; sourceTree = ""; }; 51C984612A61CE2A00B0AD9A /* MTRFabricInfoChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRFabricInfoChecker.h; sourceTree = ""; }; - 51CFDDB02AC5F78F00DA7CA5 /* EmptyDataModelHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = EmptyDataModelHandler.cpp; path = ../controller/EmptyDataModelHandler.cpp; sourceTree = ""; }; 51D0B1252B617246006E3511 /* MTRServerEndpoint.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRServerEndpoint.mm; sourceTree = ""; }; 51D0B1262B617246006E3511 /* MTRServerEndpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRServerEndpoint.h; sourceTree = ""; }; 51D0B1292B61766F006E3511 /* MTRServerEndpointTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRServerEndpointTests.m; sourceTree = ""; }; @@ -1024,7 +1050,13 @@ isa = PBXGroup; children = ( 5143041F2914CED9004DC7FE /* generic-callback-stubs.cpp */, - 51CFDDB02AC5F78F00DA7CA5 /* EmptyDataModelHandler.cpp */, + 514C79F22B62ED5500DD6D7B /* attribute-storage.cpp */, + 514C79EF2B62ADDA00DD6D7B /* descriptor.cpp */, + 514C79EC2B62ADCD00DD6D7B /* ember-compatibility-functions.cpp */, + 516415FE2B6B132200D5CE11 /* DataModelHandler.cpp */, + 514C79F52B62F0B900DD6D7B /* util.cpp */, + 514C79F82B62F60100DD6D7B /* message.cpp */, + 514C79FB2B62F94C00DD6D7B /* ota-provider.cpp */, ); name = app; path = ../../../app; @@ -1138,6 +1170,9 @@ children = ( 51D0B12C2B6177D9006E3511 /* MTRAccessGrant.h */, 51D0B12B2B6177D9006E3511 /* MTRAccessGrant.mm */, + 516416002B6B483C00D5CE11 /* MTRIMDispatch.mm */, + 516415FB2B6ACA8300D5CE11 /* MTRServerAccessControl.h */, + 516415FA2B6ACA8300D5CE11 /* MTRServerAccessControl.mm */, 51D0B1362B618CC6006E3511 /* MTRServerAttribute.h */, 51D0B1372B618CC6006E3511 /* MTRServerAttribute.mm */, 514C79FF2B64223400DD6D7B /* MTRServerAttribute_Internal.h */, @@ -1541,6 +1576,7 @@ 754F3DF427FBB94B00E60580 /* MTREventTLVValueDecoder_Internal.h in Headers */, 3CF134AF289D90FF0017A19E /* MTROperationalCertificateIssuer.h in Headers */, 5178E6822AE098520069DF72 /* MTRCommissionableBrowserResult_Internal.h in Headers */, + 516415FD2B6ACA8300D5CE11 /* MTRServerAccessControl.h in Headers */, 3CF134AB289D8DF70017A19E /* MTRDeviceAttestationInfo.h in Headers */, B2E0D7B2245B0B5C003C5B48 /* MTRManualSetupPayloadParser.h in Headers */, 3CF134A7289D8ADA0017A19E /* MTRCSRInfo.h in Headers */, @@ -1757,8 +1793,10 @@ B45373F32A9FEC1A00807602 /* server-ws.c in Sources */, 03F430AA2994113500166449 /* sysunix.c in Sources */, B45373C42A9FEA9100807602 /* dummy-callback.c in Sources */, + 514C79EE2B62ADCD00DD6D7B /* ember-compatibility-functions.cpp in Sources */, 039145E82993179300257B3E /* GetCommissionerNodeIdCommand.mm in Sources */, 0395469F2991DFC5006D42A8 /* json_reader.cpp in Sources */, + 514C79F42B62ED5500DD6D7B /* attribute-storage.cpp in Sources */, B45373D22A9FEB0C00807602 /* buflist.c in Sources */, B45373D72A9FEB0C00807602 /* lws_dll2.c in Sources */, B45373FE2A9FEC4F00807602 /* unix-fds.c in Sources */, @@ -1768,8 +1806,10 @@ 0395469E2991DFC5006D42A8 /* json_writer.cpp in Sources */, 03FB93E02A46200A0048CB35 /* DiscoverCommissionablesCommand.mm in Sources */, B45373EF2A9FEBFE00807602 /* ops-raw-skt.c in Sources */, + 516411332B6BF77700E67C05 /* MTRServerAccessControl.mm in Sources */, 037C3DD52991C2E200B7EEE2 /* CHIPCommandBridge.mm in Sources */, 039546BC2991E1CB006D42A8 /* LogCommands.cpp in Sources */, + 516411312B6BF70300E67C05 /* DataModelHandler.cpp in Sources */, B45373E12A9FEB7F00807602 /* ops-h1.c in Sources */, B45373EB2A9FEBDB00807602 /* ops-listen.c in Sources */, 0382FA2C2992F06C00247BBB /* Commands.cpp in Sources */, @@ -1804,25 +1844,29 @@ 039145E12993102B00257B3E /* main.mm in Sources */, 037C3DD42991BD5200B7EEE2 /* logging.mm in Sources */, B45374012A9FEC4F00807602 /* unix-sockets.c in Sources */, - 51CFDDB22AC5F78F00DA7CA5 /* EmptyDataModelHandler.cpp in Sources */, 03F430A82994112B00166449 /* editline.c in Sources */, B45373E92A9FEBC100807602 /* server.c in Sources */, 037C3DB32991BD5000B7EEE2 /* OpenCommissioningWindowCommand.mm in Sources */, 037C3DAE2991BD4F00B7EEE2 /* PairingCommandBridge.mm in Sources */, + 514C79FD2B62F94C00DD6D7B /* ota-provider.cpp in Sources */, B45373FB2A9FEC4F00807602 /* unix-service.c in Sources */, B45373F22A9FEC1A00807602 /* ops-ws.c in Sources */, 037C3DCA2991BD5100B7EEE2 /* CHIPCommandStorageDelegate.mm in Sources */, 037C3DCF2991BD5200B7EEE2 /* MTRError.mm in Sources */, 037C3DC72991BD5100B7EEE2 /* CHIPToolKeypair.mm in Sources */, B45373E52A9FEBA400807602 /* date.c in Sources */, + 514C79FA2B62F60100DD6D7B /* message.cpp in Sources */, + 514C79F72B62F0B900DD6D7B /* util.cpp in Sources */, B45373DC2A9FEB5300807602 /* sha-1.c in Sources */, B45373D12A9FEB0C00807602 /* alloc.c in Sources */, B45373C62A9FEA9100807602 /* sorted-usec-list.c in Sources */, 037C3DB62991BD5000B7EEE2 /* ModelCommandBridge.mm in Sources */, + 516411322B6BF75700E67C05 /* MTRIMDispatch.mm in Sources */, B45373C22A9FEA9100807602 /* vhost.c in Sources */, 037C3DB42991BD5000B7EEE2 /* DeviceControllerDelegateBridge.mm in Sources */, 039547012992D461006D42A8 /* generic-callback-stubs.cpp in Sources */, B45373D52A9FEB0C00807602 /* logs.c in Sources */, + 514C79F12B62ADDA00DD6D7B /* descriptor.cpp in Sources */, 0382FA312992FD6E00247BBB /* MTRLogging.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1833,6 +1877,7 @@ files = ( 2C8C8FC2253E0C2100797F05 /* MTRPersistentStorageDelegateBridge.mm in Sources */, 99AECC802798A57F00B6355B /* MTRCommissioningParameters.mm in Sources */, + 514C79F92B62F60100DD6D7B /* message.cpp in Sources */, 2CB7163C252E8A7C0026E2BB /* MTRDeviceControllerDelegateBridge.mm in Sources */, 997DED162695343400975E97 /* MTRThreadOperationalDataset.mm in Sources */, 515C1C6F284F9FFB00A48F0C /* MTRFramework.mm in Sources */, @@ -1840,14 +1885,18 @@ 27A53C1827FBC6920053F131 /* MTRAttestationTrustStoreBridge.mm in Sources */, 93B2CF9A2B56E45C00E4D187 /* MTRClusterNames.mm in Sources */, 998F287126D56940001846C6 /* MTRP256KeypairBridge.mm in Sources */, + 516416012B6B483C00D5CE11 /* MTRIMDispatch.mm in Sources */, 51D0B1412B61B3A4006E3511 /* MTRServerCluster.mm in Sources */, + 514C79FC2B62F94C00DD6D7B /* ota-provider.cpp in Sources */, 5136661428067D550025EDAE /* MTRDeviceControllerFactory.mm in Sources */, 51D0B1392B618CC6006E3511 /* MTRServerAttribute.mm in Sources */, 51B22C2A2740CB47008D5055 /* MTRCommandPayloadsObjc.mm in Sources */, 51F522682AE70734000C4050 /* MTRDeviceTypeMetadata.mm in Sources */, 75B765C32A1D82D30014719B /* MTRAttributeSpecifiedCheck.mm in Sources */, AF5F90FF2878D351005503FA /* MTROTAProviderDelegateBridge.mm in Sources */, + 516415FF2B6B132200D5CE11 /* DataModelHandler.cpp in Sources */, 51E95DFC2A78443C00A434F0 /* MTRSessionResumptionStorageBridge.mm in Sources */, + 514C79ED2B62ADCD00DD6D7B /* ember-compatibility-functions.cpp in Sources */, 7534F12828BFF20300390851 /* MTRDeviceAttestationDelegate.mm in Sources */, B4C8E6B72B3453AD00FCD54D /* MTRDiagnosticLogsDownloader.mm in Sources */, 2C5EEEF7268A85C400CAE3D3 /* MTRDeviceConnectionBridge.mm in Sources */, @@ -1857,10 +1906,12 @@ 3CF134A9289D8D800017A19E /* MTRCSRInfo.mm in Sources */, 991DC0892475F47D00C13860 /* MTRDeviceController.mm in Sources */, B2E0D7B7245B0B5C003C5B48 /* MTRQRCodeSetupPayloadParser.mm in Sources */, + 514C79F32B62ED5500DD6D7B /* attribute-storage.cpp in Sources */, 514304202914CED9004DC7FE /* generic-callback-stubs.cpp in Sources */, 1EDCE546289049A100E41EC9 /* MTROTAHeader.mm in Sources */, 51D0B13D2B61B2F2006E3511 /* MTRDeviceTypeRevision.mm in Sources */, 1EC4CE5D25CC26E900D7304F /* MTRBaseClusters.mm in Sources */, + 514C79F62B62F0B900DD6D7B /* util.cpp in Sources */, 51565CB22A7AD77600469F18 /* MTRDeviceControllerDataStore.mm in Sources */, 51D0B12F2B617800006E3511 /* MTRAccessGrant.mm in Sources */, 88E6C9482B6334ED001A1FE0 /* MTRMetrics.mm in Sources */, @@ -1876,11 +1927,12 @@ 5ACDDD7D27CD16D200EFD68A /* MTRClusterStateCacheContainer.mm in Sources */, 513DDB8A2761F6F900DAA01A /* MTRAttributeTLVValueDecoder.mm in Sources */, 5117DD3829A931AE00FFA1AA /* MTROperationalBrowser.mm in Sources */, + 514C79F02B62ADDA00DD6D7B /* descriptor.cpp in Sources */, 3D843757294AD25A0070D20A /* MTRCertificateInfo.mm in Sources */, 5A7947E427C0129600434CF2 /* MTRDeviceController+XPC.mm in Sources */, 5A6FEC9027B563D900F25F42 /* MTRDeviceControllerOverXPC.mm in Sources */, + 516415FC2B6ACA8300D5CE11 /* MTRServerAccessControl.mm in Sources */, B289D4222639C0D300D4E314 /* MTROnboardingPayloadParser.mm in Sources */, - 51CFDDB12AC5F78F00DA7CA5 /* EmptyDataModelHandler.cpp in Sources */, 3CF134AD289D8E570017A19E /* MTRDeviceAttestationInfo.mm in Sources */, 2C1B027A2641DB4E00780EF1 /* MTROperationalCredentialsDelegate.mm in Sources */, 7560FD1C27FBBD3F005E85B3 /* MTREventTLVValueDecoder.mm in Sources */, @@ -2022,6 +2074,7 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; STRIP_INSTALLED_PRODUCT = NO; + SYSTEM_HEADER_SEARCH_PATHS = "$(CHIP_ROOT)/src/darwin/Framework/CHIP/"; USER_HEADER_SEARCH_PATHS = ""; WARNING_CFLAGS = ( "-Wformat", @@ -2100,6 +2153,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; + SYSTEM_HEADER_SEARCH_PATHS = "$(CHIP_ROOT)/src/darwin/Framework/CHIP/"; USER_HEADER_SEARCH_PATHS = ""; WARNING_CFLAGS = ( "-Wformat", @@ -2248,9 +2302,6 @@ "$(CHIP_ROOT)/config/ios", "$(CHIP_ROOT)/src", "$(CHIP_ROOT)/src/include", - "$(CHIP_ROOT)/src/lib", - "$(CHIP_ROOT)/src/app", - "$(CHIP_ROOT)/src/app/util", "$(CHIP_ROOT)/zzz_generated/", "$(CHIP_ROOT)/zzz_generated/app-common", "$(CHIP_ROOT)/third_party/nlassert/repo/include", @@ -2419,9 +2470,6 @@ "$(CHIP_ROOT)/config/ios", "$(CHIP_ROOT)/src", "$(CHIP_ROOT)/src/include", - "$(CHIP_ROOT)/src/lib", - "$(CHIP_ROOT)/src/app", - "$(CHIP_ROOT)/src/app/util", "$(CHIP_ROOT)/zzz_generated/", "$(CHIP_ROOT)/zzz_generated/app-common", "$(CHIP_ROOT)/third_party/nlassert/repo/include", diff --git a/src/darwin/Framework/chip_xcode_build_connector.sh b/src/darwin/Framework/chip_xcode_build_connector.sh index 54e3594dfda2a2..3ab0464bb79482 100755 --- a/src/darwin/Framework/chip_xcode_build_connector.sh +++ b/src/darwin/Framework/chip_xcode_build_connector.sh @@ -93,7 +93,7 @@ done declare -a args=( 'default_configs_cosmetic=[]' # suppress colorization 'chip_crypto="boringssl"' - 'chip_build_controller_dynamic_server=true' + 'chip_build_controller_dynamic_server=false' 'chip_build_tools=false' 'chip_build_tests=false' 'chip_enable_wifi=false' diff --git a/src/platform/Darwin/CHIPDevicePlatformConfig.h b/src/platform/Darwin/CHIPDevicePlatformConfig.h index 8cb779756bf95b..15fdaa476e3645 100644 --- a/src/platform/Darwin/CHIPDevicePlatformConfig.h +++ b/src/platform/Darwin/CHIPDevicePlatformConfig.h @@ -58,9 +58,8 @@ #define CHIP_DEVICE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS 1 #endif // CHIP_DEVICE_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS -// Reserve a single dynamic endpoint that we can use to host things like OTA -// Provider server. +// Default to as many dynamic endpoints as we can manage. #if !defined(CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) || CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT == 0 #undef CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT -#define CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT 1 +#define CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT 254 #endif // CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT