Skip to content

Commit 7c70a25

Browse files
Implement support for more configurable server endpoints in Matter.framework. (project-chip#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.
1 parent 9efee65 commit 7c70a25

23 files changed

+1869
-48
lines changed

src/darwin/Framework/CHIP/MTRCallbackBridgeBase.h

+2
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ class MTRCallbackBridge : public MTRCallbackBridgeBase {
215215
}
216216

217217
if (!callbackBridge->mQueue) {
218+
ChipLogDetail(Controller, "%s %f seconds: can't dispatch response; no queue", callbackBridge->mCookie.UTF8String,
219+
-[callbackBridge->mRequestTime timeIntervalSinceNow]);
218220
if (!callbackBridge->mKeepAlive) {
219221
delete callbackBridge;
220222
}

src/darwin/Framework/CHIP/MTRDeviceController.h

+24
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,29 @@ 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+
- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint queue:(dispatch_queue_t)queue completion:(dispatch_block_t)completion MTR_NEWLY_AVAILABLE;
248+
249+
/**
250+
* Remove the given server endpoint without being notified when the removal
251+
* completes.
252+
*/
253+
- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint MTR_NEWLY_AVAILABLE;
254+
231255
/**
232256
* Compute a PASE verifier for the desired setup passcode.
233257
*

src/darwin/Framework/CHIP/MTRDeviceController.mm

+124
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"
@@ -55,20 +56,23 @@
5556

5657
#include <app-common/zap-generated/cluster-objects.h>
5758
#include <app/data-model/List.h>
59+
#include <app/server/Dnssd.h>
5860
#include <controller/CHIPDeviceController.h>
5961
#include <controller/CHIPDeviceControllerFactory.h>
6062
#include <controller/CommissioningWindowOpener.h>
6163
#include <credentials/FabricTable.h>
6264
#include <credentials/GroupDataProvider.h>
6365
#include <credentials/attestation_verifier/DacOnlyPartialAttestationVerifier.h>
6466
#include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h>
67+
#include <inet/InetInterface.h>
6568
#include <lib/core/CHIPVendorIdentifiers.hpp>
6669
#include <platform/LockTracker.h>
6770
#include <platform/PlatformManager.h>
6871
#include <setup_payload/ManualSetupPayloadGenerator.h>
6972
#include <system/SystemClock.h>
7073

7174
#include <atomic>
75+
#include <dns_sd.h>
7276

7377
#import <os/lock.h>
7478

@@ -121,6 +125,9 @@ @implementation MTRDeviceController {
121125
os_unfair_lock _deviceMapLock; // protects nodeIDToDeviceMap
122126
MTRCommissionableBrowser * _commissionableBrowser;
123127
MTRAttestationTrustStoreBridge * _attestationTrustStoreBridge;
128+
129+
// _serverEndpoints is only touched on the Matter queue.
130+
NSMutableArray<MTRServerEndpoint *> * _serverEndpoints;
124131
}
125132

126133
- (nullable instancetype)initWithParameters:(MTRDeviceControllerAbstractParameters *)parameters error:(NSError * __autoreleasing *)error
@@ -221,6 +228,7 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory
221228
_factory = factory;
222229
_deviceMapLock = OS_UNFAIR_LOCK_INIT;
223230
_nodeIDToDeviceMap = [NSMutableDictionary dictionary];
231+
_serverEndpoints = [[NSMutableArray alloc] init];
224232
_commissionableBrowser = nil;
225233

226234
_deviceControllerDelegateBridge = new MTRDeviceControllerDelegateBridge();
@@ -285,6 +293,11 @@ - (void)shutDownCppController
285293
{
286294
assertChipStackLockedByCurrentThread();
287295

296+
// Shut down all our endpoints.
297+
for (MTRServerEndpoint * endpoint in [_serverEndpoints copy]) {
298+
[self removeServerEndpointOnMatterQueue:endpoint];
299+
}
300+
288301
if (_cppCommissioner) {
289302
auto * commissionerToShutDown = _cppCommissioner;
290303
// Flag ourselves as not running before we start shutting down
@@ -947,6 +960,71 @@ - (NSData * _Nullable)attestationChallengeForDeviceID:(NSNumber *)deviceID
947960
return [self syncRunOnWorkQueueWithReturnValue:block error:nil];
948961
}
949962

963+
- (BOOL)addServerEndpoint:(MTRServerEndpoint *)endpoint
964+
{
965+
VerifyOrReturnValue([self checkIsRunning], NO);
966+
967+
if (![_factory addServerEndpoint:endpoint]) {
968+
return NO;
969+
}
970+
971+
if (![endpoint associateWithController:self]) {
972+
MTR_LOG_ERROR("Failed to associate MTRServerEndpoint with MTRDeviceController");
973+
[_factory removeServerEndpoint:endpoint];
974+
return NO;
975+
}
976+
977+
[self asyncDispatchToMatterQueue:^() {
978+
[self->_serverEndpoints addObject:endpoint];
979+
[endpoint registerMatterEndpoint];
980+
}
981+
errorHandler:^(NSError * error) {
982+
MTR_LOG_ERROR("Unexpected failure dispatching to Matter queue on running controller in addServerEndpoint");
983+
}];
984+
return YES;
985+
}
986+
987+
- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint queue:(dispatch_queue_t)queue completion:(dispatch_block_t)completion
988+
{
989+
[self removeServerEndpointInternal:endpoint queue:queue completion:completion];
990+
}
991+
992+
- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint
993+
{
994+
[self removeServerEndpointInternal:endpoint queue:nil completion:nil];
995+
}
996+
997+
- (void)removeServerEndpointInternal:(MTRServerEndpoint *)endpoint queue:(dispatch_queue_t _Nullable)queue completion:(dispatch_block_t _Nullable)completion
998+
{
999+
VerifyOrReturn([self checkIsRunning]);
1000+
1001+
// We need to unhook the endpoint from the Matter side before we can start
1002+
// tearing it down.
1003+
[self asyncDispatchToMatterQueue:^() {
1004+
[self removeServerEndpointOnMatterQueue:endpoint];
1005+
if (queue != nil && completion != nil) {
1006+
dispatch_async(queue, completion);
1007+
}
1008+
}
1009+
errorHandler:^(NSError * error) {
1010+
// Error means we got shut down, so the endpoint is removed now.
1011+
if (queue != nil && completion != nil) {
1012+
dispatch_async(queue, completion);
1013+
}
1014+
}];
1015+
}
1016+
1017+
- (void)removeServerEndpointOnMatterQueue:(MTRServerEndpoint *)endpoint
1018+
{
1019+
assertChipStackLockedByCurrentThread();
1020+
1021+
[endpoint unregisterMatterEndpoint];
1022+
[_serverEndpoints removeObject:endpoint];
1023+
[endpoint invalidate];
1024+
1025+
[_factory removeServerEndpoint:endpoint];
1026+
}
1027+
9501028
- (BOOL)checkForInitError:(BOOL)condition logMsg:(NSString *)logMsg
9511029
{
9521030
if (condition) {
@@ -1230,6 +1308,52 @@ - (void)downloadLogFromNodeWithID:(NSNumber *)nodeID
12301308
completion:completion];
12311309
}
12321310

1311+
- (NSArray<MTRAccessGrant *> *)accessGrantsForClusterPath:(MTRClusterPath *)clusterPath
1312+
{
1313+
assertChipStackLockedByCurrentThread();
1314+
1315+
for (MTRServerEndpoint * endpoint in _serverEndpoints) {
1316+
if ([clusterPath.endpoint isEqual:endpoint.endpointID]) {
1317+
return [endpoint matterAccessGrantsForCluster:clusterPath.cluster];
1318+
}
1319+
}
1320+
1321+
// Nothing matched, no grants.
1322+
return @[];
1323+
}
1324+
1325+
- (nullable NSNumber *)neededReadPrivilegeForClusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID
1326+
{
1327+
assertChipStackLockedByCurrentThread();
1328+
1329+
for (MTRServerEndpoint * endpoint in _serverEndpoints) {
1330+
for (MTRServerCluster * cluster in endpoint.serverClusters) {
1331+
if (![cluster.clusterID isEqual:clusterID]) {
1332+
continue;
1333+
}
1334+
1335+
for (MTRServerAttribute * attr in cluster.attributes) {
1336+
if (![attr.attributeID isEqual:attributeID]) {
1337+
continue;
1338+
}
1339+
1340+
return @(attr.requiredReadPrivilege);
1341+
}
1342+
}
1343+
}
1344+
1345+
return nil;
1346+
}
1347+
1348+
#ifdef DEBUG
1349+
+ (void)forceLocalhostAdvertisingOnly
1350+
{
1351+
auto interfaceIndex = chip::Inet::InterfaceId::PlatformType(kDNSServiceInterfaceIndexLocalOnly);
1352+
auto interfaceId = chip::Inet::InterfaceId(interfaceIndex);
1353+
chip::app::DnssdServer::Instance().SetInterfaceId(interfaceId);
1354+
}
1355+
#endif // DEBUG
1356+
12331357
@end
12341358

12351359
/**

0 commit comments

Comments
 (0)