Skip to content

Commit 2fba213

Browse files
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.
1 parent 38d5e18 commit 2fba213

22 files changed

+1868
-48
lines changed

src/darwin/Framework/CHIP/MTRDeviceController.h

+20
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#import <Matter/MTROperationalCertificateIssuer.h>
2323

2424
@class MTRBaseDevice;
25+
@class MTRServerEndpoint; // Defined in MTRServerEndpoint.h, which imports MTRAccessGrant.h, which imports MTRBaseClusters.h, which imports this file, so we can't import it.
2526

2627
#if MTR_PER_CONTROLLER_STORAGE_ENABLED
2728
@class MTRDeviceControllerAbstractParameters;
@@ -228,6 +229,25 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1))
228229
- (NSData * _Nullable)attestationChallengeForDeviceID:(NSNumber *)deviceID
229230
MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4));
230231

232+
/**
233+
* Add a server endpoint for this controller. The endpoint starts off enabled.
234+
*
235+
* Will fail in the following cases:
236+
*
237+
* 1) There is already an endpoint defined with the given endpoint id.
238+
* 2) There are too many endpoints defined already.
239+
*/
240+
- (BOOL)addServerEndpoint:(MTRServerEndpoint *)endpoint MTR_NEWLY_AVAILABLE;
241+
242+
/**
243+
* Remove the given server endpoint from this controller. If the endpoint is
244+
* not attached to this controller, will just call the completion and do nothing
245+
* else.
246+
*
247+
* The endpoint being removed does not need to be disabled first.
248+
*/
249+
- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint queue:(dispatch_queue_t)queue completion:(dispatch_block_t)completion MTR_NEWLY_AVAILABLE;
250+
231251
/**
232252
* Compute a PASE verifier for the desired setup passcode.
233253
*

src/darwin/Framework/CHIP/MTRDeviceController.mm

+98
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#import "MTROperationalCredentialsDelegate.h"
4242
#import "MTRP256KeypairBridge.h"
4343
#import "MTRPersistentStorageDelegateBridge.h"
44+
#import "MTRServerEndpoint_Internal.h"
4445
#import "MTRSetupPayload.h"
4546
#import "NSDataSpanConversion.h"
4647
#import "NSStringSpanConversion.h"
@@ -121,6 +122,9 @@ @implementation MTRDeviceController {
121122
os_unfair_lock _deviceMapLock; // protects nodeIDToDeviceMap
122123
MTRCommissionableBrowser * _commissionableBrowser;
123124
MTRAttestationTrustStoreBridge * _attestationTrustStoreBridge;
125+
126+
// _serverEndpoints is only touched on the Matter queue.
127+
NSMutableArray<MTRServerEndpoint *> * _serverEndpoints;
124128
}
125129

126130
- (nullable instancetype)initWithParameters:(MTRDeviceControllerAbstractParameters *)parameters error:(NSError * __autoreleasing *)error
@@ -221,6 +225,7 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory
221225
_factory = factory;
222226
_deviceMapLock = OS_UNFAIR_LOCK_INIT;
223227
_nodeIDToDeviceMap = [NSMutableDictionary dictionary];
228+
_serverEndpoints = [[NSMutableArray alloc] init];
224229
_commissionableBrowser = nil;
225230

226231
_deviceControllerDelegateBridge = new MTRDeviceControllerDelegateBridge();
@@ -285,6 +290,11 @@ - (void)shutDownCppController
285290
{
286291
assertChipStackLockedByCurrentThread();
287292

293+
// Shut down all our endpoints.
294+
for (MTRServerEndpoint * endpoint in [_serverEndpoints copy]) {
295+
[self removeServerEndpointOnMatterQueue:endpoint];
296+
}
297+
288298
if (_cppCommissioner) {
289299
auto * commissionerToShutDown = _cppCommissioner;
290300
// Flag ourselves as not running before we start shutting down
@@ -947,6 +957,57 @@ - (NSData * _Nullable)attestationChallengeForDeviceID:(NSNumber *)deviceID
947957
return [self syncRunOnWorkQueueWithReturnValue:block error:nil];
948958
}
949959

960+
- (BOOL)addServerEndpoint:(MTRServerEndpoint *)endpoint
961+
{
962+
VerifyOrReturnValue([self checkIsRunning], NO);
963+
964+
if (![_factory addServerEndpoint:endpoint]) {
965+
return NO;
966+
}
967+
968+
if (![endpoint associateWithController:self]) {
969+
MTR_LOG_ERROR("Failed to associate MTRServerEndpoint with MTRDeviceController");
970+
[_factory removeServerEndpoint:endpoint];
971+
return NO;
972+
}
973+
974+
[self asyncDispatchToMatterQueue:^() {
975+
[self->_serverEndpoints addObject:endpoint];
976+
[endpoint registerMatterEndpoint];
977+
}
978+
errorHandler:^(NSError * error) {
979+
MTR_LOG_ERROR("Unexpected failure dispatching to Matter queue on running controller in addServerEndpoint");
980+
}];
981+
return YES;
982+
}
983+
984+
- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint queue:(dispatch_queue_t)queue completion:(dispatch_block_t)completion MTR_NEWLY_AVAILABLE
985+
{
986+
VerifyOrReturn([self checkIsRunning]);
987+
988+
// We need to unhook the endpoint from the Matter side before we can start
989+
// tearing it down.
990+
[self asyncDispatchToMatterQueue:^() {
991+
[self removeServerEndpointOnMatterQueue:endpoint];
992+
dispatch_async(queue, completion);
993+
}
994+
errorHandler:^(NSError * error) {
995+
// Error means we got shut down, so the endpoint is removed now.
996+
dispatch_async(queue, completion);
997+
}];
998+
}
999+
1000+
- (void)removeServerEndpointOnMatterQueue:(MTRServerEndpoint *)endpoint
1001+
{
1002+
assertChipStackLockedByCurrentThread();
1003+
1004+
[endpoint unregisterMatterEndpoint];
1005+
[_serverEndpoints removeObject:endpoint];
1006+
[endpoint invalidate];
1007+
1008+
[_factory removeServerEndpoint:endpoint];
1009+
}
1010+
9501011
- (BOOL)checkForInitError:(BOOL)condition logMsg:(NSString *)logMsg
9511012
{
9521013
if (condition) {
@@ -1230,6 +1291,43 @@ - (void)downloadLogFromNodeWithID:(NSNumber *)nodeID
12301291
completion:completion];
12311292
}
12321293

1294+
- (NSArray<MTRAccessGrant *> *)accessGrantsForClusterPath:(MTRClusterPath *)clusterPath
1295+
{
1296+
assertChipStackLockedByCurrentThread();
1297+
1298+
for (MTRServerEndpoint * endpoint in _serverEndpoints) {
1299+
if ([clusterPath.endpoint isEqual:endpoint.endpointID]) {
1300+
return [endpoint matterAccessGrantsForCluster:clusterPath.cluster];
1301+
}
1302+
}
1303+
1304+
// Nothing matched, no grants.
1305+
return @[];
1306+
}
1307+
1308+
- (nullable NSNumber *)neededReadPrivilegeForClusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID
1309+
{
1310+
assertChipStackLockedByCurrentThread();
1311+
1312+
for (MTRServerEndpoint * endpoint in _serverEndpoints) {
1313+
for (MTRServerCluster * cluster in endpoint.serverClusters) {
1314+
if (![cluster.clusterID isEqual:clusterID]) {
1315+
continue;
1316+
}
1317+
1318+
for (MTRServerAttribute * attr in cluster.attributes) {
1319+
if (![attr.attributeID isEqual:attributeID]) {
1320+
continue;
1321+
}
1322+
1323+
return @(attr.requiredReadPrivilege);
1324+
}
1325+
}
1326+
}
1327+
1328+
return nil;
1329+
}
1330+
12331331
@end
12341332

12351333
/**

0 commit comments

Comments
 (0)