Skip to content

Commit fd216ec

Browse files
Add a way to forget a node ID to Matter.framework. (project-chip#37198)
The intent is to tear down any MTRDevice connection to the node and remove any stored data for the node ID.
1 parent 12235cd commit fd216ec

9 files changed

+109
-13
lines changed

src/darwin/Framework/CHIP/MTRDeviceController.h

+6
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,12 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1))
269269
*/
270270
- (void)removeServerEndpoint:(MTRServerEndpoint *)endpoint MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6));
271271

272+
/**
273+
* Forget any information we have about the device with the given node ID. That
274+
* includes clearing any information we have stored about it.
275+
*/
276+
- (void)forgetDeviceWithNodeID:(NSNumber *)nodeID MTR_NEWLY_AVAILABLE;
277+
272278
/**
273279
* Compute a PASE verifier for the desired setup passcode.
274280
*

src/darwin/Framework/CHIP/MTRDeviceController.mm

+12
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,18 @@ - (MTRDevice *)deviceForNodeID:(NSNumber *)nodeID
386386
return [self _deviceForNodeID:nodeID createIfNeeded:YES];
387387
}
388388

389+
- (void)forgetDeviceWithNodeID:(NSNumber *)nodeID
390+
{
391+
MTRDevice * deviceToRemove;
392+
{
393+
std::lock_guard lock(*self.deviceMapLock);
394+
deviceToRemove = [_nodeIDToDeviceMap objectForKey:nodeID];
395+
}
396+
if (deviceToRemove != nil) {
397+
[self removeDevice:deviceToRemove];
398+
}
399+
}
400+
389401
- (void)removeDevice:(MTRDevice *)device
390402
{
391403
std::lock_guard lock(*self.deviceMapLock);

src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.h

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ typedef void (^MTRDeviceControllerDataStoreClusterDataHandler)(NSDictionary<NSNu
6161
- (nullable MTRCASESessionResumptionInfo *)findResumptionInfoByNodeID:(NSNumber *)nodeID;
6262
- (nullable MTRCASESessionResumptionInfo *)findResumptionInfoByResumptionID:(NSData *)resumptionID;
6363
- (void)storeResumptionInfo:(MTRCASESessionResumptionInfo *)resumptionInfo;
64+
- (void)clearResumptionInfoForNodeID:(NSNumber *)nodeID;
6465
- (void)clearAllResumptionInfo;
6566

6667
/**
@@ -92,6 +93,7 @@ typedef void (^MTRDeviceControllerDataStoreClusterDataHandler)(NSDictionary<NSNu
9293
*/
9394
- (nullable NSDictionary<NSString *, id> *)getStoredDeviceDataForNodeID:(NSNumber *)nodeID;
9495
- (void)storeDeviceData:(NSDictionary<NSString *, id> *)data forNodeID:(NSNumber *)nodeID;
96+
- (void)clearDeviceDataForNodeID:(NSNumber *)nodeID;
9597

9698
/**
9799
* Mechanism for an API client to perform a block after previous async operations (writes) on the storage queue have executed.

src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.mm

+41-13
Original file line numberDiff line numberDiff line change
@@ -228,24 +228,38 @@ - (void)clearAllResumptionInfo
228228
// Can we do less dispatch? We would need to have a version of
229229
// _findResumptionInfoWithKey that assumes we are already on the right queue.
230230
for (NSNumber * nodeID in _nodesWithResumptionInfo) {
231-
auto * oldInfo = [self findResumptionInfoByNodeID:nodeID];
232-
if (oldInfo != nil) {
233-
dispatch_sync(_storageDelegateQueue, ^{
234-
[_storageDelegate controller:controller
235-
removeValueForKey:ResumptionByResumptionIDKey(oldInfo.resumptionID)
236-
securityLevel:MTRStorageSecurityLevelSecure
237-
sharingType:MTRStorageSharingTypeNotShared];
238-
[_storageDelegate controller:controller
239-
removeValueForKey:ResumptionByNodeIDKey(oldInfo.nodeID)
240-
securityLevel:MTRStorageSecurityLevelSecure
241-
sharingType:MTRStorageSharingTypeNotShared];
242-
});
243-
}
231+
[self _clearResumptionInfoForNodeID:nodeID controller:controller];
244232
}
245233

246234
[_nodesWithResumptionInfo removeAllObjects];
247235
}
248236

237+
- (void)clearResumptionInfoForNodeID:(NSNumber *)nodeID
238+
{
239+
MTRDeviceController * controller = _controller;
240+
VerifyOrReturn(controller != nil); // No way to call delegate without controller.
241+
242+
[self _clearResumptionInfoForNodeID:nodeID controller:controller];
243+
[_nodesWithResumptionInfo removeObject:nodeID];
244+
}
245+
246+
- (void)_clearResumptionInfoForNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller
247+
{
248+
auto * oldInfo = [self findResumptionInfoByNodeID:nodeID];
249+
if (oldInfo != nil) {
250+
dispatch_sync(_storageDelegateQueue, ^{
251+
[_storageDelegate controller:controller
252+
removeValueForKey:ResumptionByResumptionIDKey(oldInfo.resumptionID)
253+
securityLevel:MTRStorageSecurityLevelSecure
254+
sharingType:MTRStorageSharingTypeNotShared];
255+
[_storageDelegate controller:controller
256+
removeValueForKey:ResumptionByNodeIDKey(oldInfo.nodeID)
257+
securityLevel:MTRStorageSecurityLevelSecure
258+
sharingType:MTRStorageSharingTypeNotShared];
259+
});
260+
}
261+
}
262+
249263
- (CHIP_ERROR)storeLastLocallyUsedNOC:(MTRCertificateTLVBytes)noc
250264
{
251265
MTRDeviceController * controller = _controller;
@@ -1169,6 +1183,20 @@ - (void)storeDeviceData:(NSDictionary<NSString *, id> *)data forNodeID:(NSNumber
11691183
});
11701184
}
11711185

1186+
- (void)clearDeviceDataForNodeID:(NSNumber *)nodeID
1187+
{
1188+
dispatch_async(_storageDelegateQueue, ^{
1189+
MTRDeviceController * controller = self->_controller;
1190+
VerifyOrReturn(controller != nil); // No way to call delegate without controller.
1191+
1192+
// Ignore store failures, since they are not actionable for us here.
1193+
[self->_storageDelegate controller:controller
1194+
removeValueForKey:[self _deviceDataKeyForNodeID:nodeID]
1195+
securityLevel:MTRStorageSecurityLevelSecure
1196+
sharingType:MTRStorageSharingTypeNotShared];
1197+
});
1198+
}
1199+
11721200
- (void)synchronouslyPerformBlock:(void (^_Nullable)(void))block
11731201
{
11741202
dispatch_sync(_storageDelegateQueue, ^{

src/darwin/Framework/CHIP/MTRDeviceController_Concrete.mm

+15
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,21 @@ - (MTRDevice *)_setupDeviceForNodeID:(NSNumber *)nodeID prefetchedClusterData:(N
11931193
return deviceToReturn;
11941194
}
11951195

1196+
- (void)forgetDeviceWithNodeID:(NSNumber *)nodeID
1197+
{
1198+
MTR_LOG("%@: Forgetting device with node ID: %@", self, nodeID);
1199+
1200+
// Tear down any existing MTRDevice for this nodeID first, so we don't run
1201+
// into issues with it storing data after we have deleted it.
1202+
[super forgetDeviceWithNodeID:nodeID];
1203+
1204+
if (_controllerDataStore) {
1205+
[_controllerDataStore clearResumptionInfoForNodeID:nodeID];
1206+
[_controllerDataStore clearDeviceDataForNodeID:nodeID];
1207+
[_controllerDataStore clearStoredClusterDataForNodeID:nodeID];
1208+
}
1209+
}
1210+
11961211
#ifdef DEBUG
11971212
- (NSDictionary<NSNumber *, NSNumber *> *)unitTestGetDeviceAttributeCounts
11981213
{

src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm

+11
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ @implementation MTRDeviceController_XPC
6060
: (NSDictionary *) controllerState, updateControllerConfiguration
6161
: (NSDictionary *) controllerState)
6262

63+
MTR_DEVICECONTROLLER_SIMPLE_REMOTE_XPC_COMMAND(deleteNodeID
64+
: (NSNumber *) nodeID, deleteNodeID
65+
: (NSNumber *) nodeID)
66+
6367
- (void)_updateRegistrationInfo
6468
{
6569
NSMutableDictionary * registrationInfo = [NSMutableDictionary dictionary];
@@ -95,6 +99,13 @@ - (void)removeDevice:(MTRDevice *)device
9599
[self _updateRegistrationInfo];
96100
}
97101

102+
- (void)forgetDeviceWithNodeID:(NSNumber *)nodeID
103+
{
104+
MTR_LOG("%@: Forgetting device with node ID: %@", self, nodeID);
105+
[self deleteNodeID:nodeID];
106+
[super forgetDeviceWithNodeID:nodeID];
107+
}
108+
98109
#pragma mark - XPC
99110
@synthesize controllerNodeID = _controllerNodeID;
100111
@synthesize compressedFabricID = _compressedFabricID;

src/darwin/Framework/CHIP/XPC Protocol/MTRXPCServerProtocol.h

+2
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ MTR_AVAILABLE(ios(18.2), macos(15.2), watchos(11.2), tvos(18.2))
7171
// - (oneway void)deviceController:(NSUUID *)controller addServerEndpoint:(MTRServerEndpoint *)endpoint withReply:(void (^)(BOOL success))reply;
7272
// - (oneway void)deviceController:(NSUUID *)controller removeServerEndpoint:(MTRServerEndpoint *)endpoint;
7373

74+
- (oneway void)deviceController:(NSUUID *)controller deleteNodeID:(NSNumber *)nodeID MTR_NEWLY_AVAILABLE;
75+
7476
- (oneway void)deviceController:(NSUUID *)controller registerNodeID:(NSNumber *)nodeID;
7577
- (oneway void)deviceController:(NSUUID *)controller unregisterNodeID:(NSNumber *)nodeID;
7678
- (oneway void)deviceController:(NSUUID *)controller updateControllerConfiguration:(NSDictionary *)controllerState;

src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m

+16
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,22 @@ - (void)test010_TestDataStoreMTRDeviceWithBulkReadWrite
17641764
}
17651765
XCTAssertTrue(totalAttributes > 300);
17661766

1767+
// Now try forgetting this device and make sure all the info we had for it
1768+
// goes away.
1769+
NSNumber * deviceID = @(17);
1770+
__auto_type * dataStore = controller.controllerDataStore;
1771+
XCTAssertNotNil(deviceAttributeCounts[deviceID]);
1772+
XCTAssertNotNil([dataStore findResumptionInfoByNodeID:deviceID]);
1773+
XCTAssertNotNil([dataStore getStoredDeviceDataForNodeID:deviceID]);
1774+
XCTAssertNotNil([dataStore getStoredClusterDataForNodeID:deviceID]);
1775+
1776+
[controller forgetDeviceWithNodeID:deviceID];
1777+
deviceAttributeCounts = [controller unitTestGetDeviceAttributeCounts];
1778+
XCTAssertNil(deviceAttributeCounts[deviceID]);
1779+
XCTAssertNil([dataStore findResumptionInfoByNodeID:deviceID]);
1780+
XCTAssertNil([dataStore getStoredDeviceDataForNodeID:deviceID]);
1781+
XCTAssertNil([dataStore getStoredClusterDataForNodeID:deviceID]);
1782+
17671783
[controller shutdown];
17681784
XCTAssertFalse([controller isRunning]);
17691785
}

src/darwin/Framework/CHIPTests/TestHelpers/MTRTestDeclarations.h

+4
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ NS_ASSUME_NONNULL_BEGIN
2626

2727
#pragma mark - Declarations for internal methods
2828

29+
@class MTRCASESessionResumptionInfo;
30+
2931
// MTRDeviceControllerDataStore.h includes C++ header, and so we need to declare the methods separately
3032
@protocol MTRDeviceControllerDataStoreAttributeStoreMethods
33+
- (nullable MTRCASESessionResumptionInfo *)findResumptionInfoByNodeID:(NSNumber *)nodeID;
3134
- (nullable NSDictionary<MTRClusterPath *, MTRDeviceClusterData *> *)getStoredClusterDataForNodeID:(NSNumber *)nodeID;
3235
- (void)storeClusterData:(NSDictionary<MTRClusterPath *, MTRDeviceClusterData *> *)clusterData forNodeID:(NSNumber *)nodeID;
3336
- (void)clearStoredClusterDataForNodeID:(NSNumber *)nodeID;
@@ -39,6 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
3942
- (nullable NSArray<NSNumber *> *)_fetchEndpointIndexForNodeID:(NSNumber *)nodeID;
4043
- (nullable NSArray<NSNumber *> *)_fetchClusterIndexForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID;
4144
- (nullable MTRDeviceClusterData *)_fetchClusterDataForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID;
45+
- (nullable NSDictionary<NSString *, id> *)getStoredDeviceDataForNodeID:(NSNumber *)nodeID;
4246
@end
4347

4448
// Declare internal methods for testing

0 commit comments

Comments
 (0)