Skip to content

Commit 01f09c2

Browse files
committed
Add support to intercept attribute report and prune any removed endpoints and clusters and attributes from both persisted clusters data and data storage
- When an attribute report is received, check if there are any changes to parts list or server list for the descriptor cluster or attribute list for any cluster. - If any endpoints were removed, make sure to delete the cluster index for the endpoint and all cluster data for that endpoint should be deleted. - If any clusters were removed from an endpoint, make sure to delete the cluster from the cluster index for that endpoint and also clear the cluster data for that cluster. - If any attributes were removed from a cluster, make sure to update the cluster data and delete the entry for the attribute that was removed.
1 parent f075b9b commit 01f09c2

6 files changed

+1264
-5
lines changed

src/darwin/Framework/CHIP/MTRDevice.mm

+197-2
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ - (void)storeValue:(MTRDeviceDataValueDictionary _Nullable)value forAttribute:(N
196196
_attributes[attribute] = value;
197197
}
198198

199+
- (void)_removeValueForAttribute:(NSNumber *)attribute
200+
{
201+
[_attributes removeObjectForKey:attribute];
202+
}
203+
199204
- (NSDictionary<NSNumber *, MTRDeviceDataValueDictionary> *)attributes
200205
{
201206
return _attributes;
@@ -1374,6 +1379,18 @@ - (void)_setCachedAttributeValue:(MTRDeviceDataValueDictionary _Nullable)value f
13741379
_clusterDataToPersist[clusterPath] = clusterData;
13751380
}
13761381

1382+
- (void)_removeCachedAttributeValue:(MTRClusterPath *)clusterPath forPath:(MTRAttributePath *)attributePath
1383+
{
1384+
os_unfair_lock_assert_owner(&self->_lock);
1385+
1386+
if (_clusterDataToPersist == nil) {
1387+
return;
1388+
}
1389+
auto * clusterData = [_clusterDataToPersist objectForKey:clusterPath];
1390+
[clusterData _removeValueForAttribute:attributePath.attribute];
1391+
[_clusterDataToPersist setObject:clusterData forKey:clusterPath];
1392+
}
1393+
13771394
- (void)_createDataVersionFilterListFromDictionary:(NSDictionary<MTRClusterPath *, NSNumber *> *)dataVersions dataVersionFilterList:(DataVersionFilter **)dataVersionFilterList count:(size_t *)count sizeReduction:(size_t)sizeReduction
13781395
{
13791396
size_t maxDataVersionFilterSize = dataVersions.count;
@@ -2431,7 +2448,7 @@ - (BOOL)_attributeDataValue:(NSDictionary *)one isEqualToDataValue:(NSDictionary
24312448
}
24322449

24332450
// Utility to return data value dictionary without data version
2434-
- (NSDictionary *)_dataValueWithoutDataVersion:(NSDictionary *)attributeValue;
2451+
- (NSDictionary *)_dataValueWithoutDataVersion:(NSDictionary *)attributeValue
24352452
{
24362453
// Sanity check for nil - return the same input to fail gracefully
24372454
if (!attributeValue || !attributeValue[MTRTypeKey]) {
@@ -2493,6 +2510,137 @@ - (BOOL)_attributeAffectsDeviceConfiguration:(MTRAttributePath *)attributePath
24932510
return NO;
24942511
}
24952512

2513+
- (BOOL)_needsPruningOfEndpointsAndClusters:(MTRAttributePath *)attributePath
2514+
{
2515+
// Check for attributes in the descriptor cluster that could cause removal of endpoints and clusters.
2516+
if (attributePath.cluster.unsignedLongValue == MTRClusterIDTypeDescriptorID) {
2517+
switch (attributePath.attribute.unsignedLongValue) {
2518+
case MTRAttributeIDTypeClusterDescriptorAttributePartsListID:
2519+
case MTRAttributeIDTypeClusterDescriptorAttributeServerListID:
2520+
return YES;
2521+
}
2522+
}
2523+
2524+
// Check for global attribute - attribute list that could cause removal of attributes.
2525+
switch (attributePath.attribute.unsignedLongValue) {
2526+
case MTRAttributeIDTypeGlobalAttributeAttributeListID:
2527+
return YES;
2528+
}
2529+
return NO;
2530+
}
2531+
2532+
- (void)_pruneOrphanedEndpointsAndClusters:(MTRAttributePath *)attributePath
2533+
previousValue:(NSDictionary *)previousValue
2534+
attributeDataValue:(NSDictionary *)attributeDataValue
2535+
{
2536+
os_unfair_lock_assert_owner(&self->_lock);
2537+
2538+
if (_persistedClusters == nil || _persistedClusterData == nil || !previousValue.count)
2539+
{
2540+
return;
2541+
}
2542+
// Check if parts list changed or server list changed for the descriptor cluster or the attribute list changed for a cluster.
2543+
// If yes, we might need to prune any deleted endpoints, clusters or attributes from the storage and persisted cluster data.
2544+
if (attributePath.cluster.unsignedLongValue == MTRClusterIDTypeDescriptorID) {
2545+
switch (attributePath.attribute.unsignedLongValue) {
2546+
2547+
// If the parts list changed and one or more endpoints were removed, remove all the clusters in _persistedClusters and _persistedClusterData for all those endpoints.
2548+
// Also remove it from the data store.
2549+
case MTRAttributeIDTypeClusterDescriptorAttributePartsListID:
2550+
{
2551+
NSMutableSet * toBeRemovedEndpoints = [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:previousValue]];
2552+
NSSet * endpointsOnDevice = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:attributeDataValue]];
2553+
[toBeRemovedEndpoints minusSet:endpointsOnDevice];
2554+
2555+
for (NSNumber * endpoint in toBeRemovedEndpoints)
2556+
{
2557+
NSMutableSet<MTRClusterPath *> * clusterPathsToRemove = [[NSMutableSet alloc]init];
2558+
for (MTRClusterPath * path in _persistedClusters)
2559+
{
2560+
if ([path.endpoint isEqualToNumber:endpoint])
2561+
{
2562+
[clusterPathsToRemove addObject:path];
2563+
[_persistedClusterData removeObjectForKey:path];
2564+
[self.deviceController.controllerDataStore clearStoredClusterDataForNodeIDWithEndpointID:self.nodeID endpointID:endpoint];
2565+
}
2566+
}
2567+
[_persistedClusters minusSet:clusterPathsToRemove];
2568+
}
2569+
break;
2570+
}
2571+
2572+
// If the server list changed and clusters were removed, remove the clusters from the _persistedClusters and _persistedClusterData for that endpoint
2573+
// Also remove it from the data store.
2574+
case MTRAttributeIDTypeClusterDescriptorAttributeServerListID:
2575+
{
2576+
NSMutableSet * toBeRemovedClusters= [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:previousValue]];
2577+
NSSet * clustersOnDevice = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:attributeDataValue]];
2578+
[toBeRemovedClusters minusSet:clustersOnDevice];
2579+
2580+
NSMutableSet<MTRClusterPath *> * clusterPathsToRemove = [[NSMutableSet alloc]init];
2581+
for (NSNumber * cluster in toBeRemovedClusters)
2582+
{
2583+
for (MTRClusterPath * path in _persistedClusters)
2584+
{
2585+
if ([path.endpoint isEqualToNumber:attributePath.endpoint] && [path.cluster isEqualToNumber:cluster])
2586+
{
2587+
[clusterPathsToRemove addObject:path];
2588+
[_persistedClusterData removeObjectForKey:path];
2589+
2590+
[self.deviceController.controllerDataStore clearStoredClusterDataForNodeIDWithClusterID:self.nodeID endpointID:path.endpoint clusterID:path.cluster];
2591+
}
2592+
}
2593+
}
2594+
[_persistedClusters minusSet:clusterPathsToRemove];
2595+
break;
2596+
}
2597+
}
2598+
}
2599+
2600+
switch (attributePath.attribute.unsignedLongValue) {
2601+
// If the attribute list changed and attributes were removed, remove the attributes from the _persistedClusterData for that cluster and endpoint.
2602+
// Also remove it from the data store cluster data.
2603+
case MTRAttributeIDTypeGlobalAttributeAttributeListID:
2604+
{
2605+
NSMutableSet * toBeRemovedAttributes= [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:[self _cachedAttributeValueForPath:attributePath]]];
2606+
NSSet * attributesOnDevice = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:attributeDataValue]];
2607+
2608+
[toBeRemovedAttributes minusSet:attributesOnDevice];
2609+
for (NSNumber * attribute in toBeRemovedAttributes)
2610+
{
2611+
for (MTRClusterPath * path in _persistedClusters)
2612+
{
2613+
if ([path.endpoint isEqualToNumber:attributePath.endpoint] && [path.cluster isEqualToNumber:attributePath.cluster])
2614+
{
2615+
MTRDeviceClusterData * clusterData = [self _clusterDataForPath:path];
2616+
if (clusterData == nil)
2617+
{
2618+
return;
2619+
}
2620+
[clusterData _removeValueForAttribute:attribute];
2621+
[self->_persistedClusterData setObject:clusterData forKey:path];
2622+
2623+
NSDictionary<MTRClusterPath *, MTRDeviceClusterData *> * dataStoreClusterData = [self.deviceController.controllerDataStore getStoredClusterDataForNodeID:self.nodeID];
2624+
NSMutableDictionary<MTRClusterPath *, MTRDeviceClusterData *> * dataStoreClusterDataCopy = [dataStoreClusterData mutableCopy];
2625+
for (MTRClusterPath * dataStorePath in dataStoreClusterData)
2626+
{
2627+
if ([dataStorePath isEqualTo:path])
2628+
{
2629+
[dataStoreClusterDataCopy removeObjectForKey:path];
2630+
[dataStoreClusterDataCopy setObject:clusterData forKey:path];
2631+
[self.deviceController.controllerDataStore storeClusterData:dataStoreClusterDataCopy forNodeID:self.nodeID];
2632+
dataStoreClusterData = [NSMutableDictionary dictionaryWithDictionary:[self.deviceController.controllerDataStore getStoredClusterDataForNodeID:self.nodeID]];
2633+
}
2634+
}
2635+
[self _removeCachedAttributeValue:path forPath:attributePath];
2636+
}
2637+
}
2638+
}
2639+
break;
2640+
}
2641+
}
2642+
}
2643+
24962644
// assume lock is held
24972645
- (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSString *, id> *> *)reportedAttributeValues
24982646
{
@@ -2544,13 +2692,19 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
25442692
BOOL readCacheValueChanged = ![self _attributeDataValue:attributeDataValue isEqualToDataValue:previousValue];
25452693
// Now that we have grabbed previousValue, update our cache with the attribute value.
25462694
if (readCacheValueChanged) {
2547-
[self _setCachedAttributeValue:attributeDataValue forPath:attributePath];
2695+
if ([self _needsPruningOfEndpointsAndClusters:attributePath])
2696+
{
2697+
previousValue = [self _dataValueWithoutDataVersion:previousValue];
2698+
[self _pruneOrphanedEndpointsAndClusters:attributePath previousValue:previousValue attributeDataValue:attributeDataValue];
2699+
}
2700+
25482701
if (!_deviceConfigurationChanged) {
25492702
_deviceConfigurationChanged = [self _attributeAffectsDeviceConfiguration:attributePath];
25502703
if (_deviceConfigurationChanged) {
25512704
MTR_LOG_INFO("Device configuration changed due to changes in attribute %@", attributePath);
25522705
}
25532706
}
2707+
[self _setCachedAttributeValue:attributeDataValue forPath:attributePath];
25542708
}
25552709

25562710
#ifdef DEBUG
@@ -2660,6 +2814,47 @@ - (void)setPersistedClusterData:(NSDictionary<MTRClusterPath *, MTRDeviceCluster
26602814
}
26612815
}
26622816

2817+
- (void)_removePersistedClusterDataForPath:(MTRClusterPath *)path
2818+
{
2819+
os_unfair_lock_assert_owner(&self->_lock);
2820+
if (_persistedClusters == nil || _persistedClusterData == nil)
2821+
{
2822+
return;
2823+
}
2824+
2825+
[_persistedClusterData removeObjectForKey:path];
2826+
[_persistedClusters removeObject:path];
2827+
}
2828+
2829+
- (NSMutableSet<MTRClusterPath *> *)_getPersistedClusters
2830+
{
2831+
std::lock_guard lock(_lock);
2832+
2833+
return _persistedClusters;
2834+
}
2835+
2836+
- (MTRDeviceClusterData *)_getPersistedClusterDataForPath:(MTRClusterPath *)path
2837+
{
2838+
std::lock_guard lock(_lock);
2839+
2840+
if ([_persistedClusters containsObject:path])
2841+
{
2842+
return [_persistedClusterData objectForKey:path];
2843+
}
2844+
return nil;
2845+
}
2846+
2847+
- (BOOL)_persistedClusterContains:(MTRClusterPath *)path
2848+
{
2849+
std::lock_guard lock(_lock);
2850+
2851+
if ([_persistedClusters containsObject:path])
2852+
{
2853+
return YES;
2854+
}
2855+
return NO;
2856+
}
2857+
26632858
- (BOOL)deviceCachePrimed
26642859
{
26652860
std::lock_guard lock(_lock);

src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.h

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ typedef void (^MTRDeviceControllerDataStoreClusterDataHandler)(NSDictionary<NSNu
7676
- (nullable MTRDeviceClusterData *)getStoredClusterDataForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID;
7777
- (void)storeClusterData:(NSDictionary<MTRClusterPath *, MTRDeviceClusterData *> *)clusterData forNodeID:(NSNumber *)nodeID;
7878
- (void)clearStoredClusterDataForNodeID:(NSNumber *)nodeID;
79+
- (void)clearStoredClusterDataForNodeIDWithEndpointID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID;
80+
- (void)clearStoredClusterDataForNodeIDWithClusterID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID;
7981
- (void)clearAllStoredClusterData;
8082

8183
@end

src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.mm

+80-3
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,25 @@ - (BOOL)_storeEndpointIndex:(NSArray<NSNumber *> *)endpointIndex forNodeID:(NSNu
446446
return [self _storeAttributeCacheValue:endpointIndex forKey:[self _endpointIndexKeyForNodeID:nodeID]];
447447
}
448448

449+
- (BOOL)_deleteEndpointIndex:(NSNumber *)endpointID forNodeID:(NSNumber *)nodeID
450+
{
451+
dispatch_assert_queue(_storageDelegateQueue);
452+
453+
if (!endpointID || !nodeID) {
454+
MTR_LOG_ERROR("%s: unexpected nil input", __func__);
455+
return NO;
456+
}
457+
458+
NSMutableArray * endpointIndex = [NSMutableArray arrayWithArray:[self _fetchEndpointIndexForNodeID:nodeID]];
459+
if (endpointIndex == nil)
460+
{
461+
return NO;
462+
}
463+
464+
[endpointIndex removeObject:endpointID];
465+
return [self _storeAttributeCacheValue:endpointIndex forKey:[self _endpointIndexKeyForNodeID:nodeID]];
466+
}
467+
449468
- (BOOL)_deleteEndpointIndexForNodeID:(NSNumber *)nodeID
450469
{
451470
dispatch_assert_queue(_storageDelegateQueue);
@@ -497,7 +516,6 @@ - (BOOL)_deleteClusterIndexForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)e
497516
MTR_LOG_ERROR("%s: unexpected nil input", __func__);
498517
return NO;
499518
}
500-
501519
return [self _removeAttributeCacheValueForKey:[self _clusterIndexKeyForNodeID:nodeID endpointID:endpointID]];
502520
}
503521

@@ -540,8 +558,8 @@ - (BOOL)_deleteClusterDataForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)en
540558
MTR_LOG_ERROR("%s: unexpected nil input", __func__);
541559
return NO;
542560
}
543-
544-
return [self _removeAttributeCacheValueForKey:[self _clusterDataKeyForNodeID:nodeID endpointID:endpointID clusterID:clusterID]];
561+
BOOL value = [self _removeAttributeCacheValueForKey:[self _clusterDataKeyForNodeID:nodeID endpointID:endpointID clusterID:clusterID]];
562+
return value;
545563
}
546564

547565
#pragma - Attribute Cache management
@@ -699,6 +717,65 @@ - (void)clearStoredClusterDataForNodeID:(NSNumber *)nodeID
699717
});
700718
}
701719

720+
- (BOOL)_clearStoredClusterDataForNodeIDWithEndpointID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID
721+
{
722+
dispatch_assert_queue(_storageDelegateQueue);
723+
NSArray<NSNumber *> * clusterIndex = [self _fetchClusterIndexForNodeID:nodeID endpointID:endpointID];
724+
725+
BOOL success = NO;
726+
for (NSNumber * clusterID in clusterIndex) {
727+
success = [self _deleteClusterDataForNodeID:nodeID endpointID:endpointID clusterID:clusterID];
728+
if (!success) {
729+
MTR_LOG_INFO("_clearStoredClusterDataForNodeIDWithEndpointID: Delete failed for clusterData @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, clusterID.unsignedLongValue);
730+
}
731+
}
732+
733+
success = [self _deleteClusterIndexForNodeID:nodeID endpointID:endpointID];
734+
if (!success) {
735+
MTR_LOG_INFO("Delete failed for clusterIndex @ node 0x%016llX endpoint %u", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue);
736+
}
737+
738+
success = [self _deleteEndpointIndex:endpointID forNodeID:(nodeID)];
739+
if (!success) {
740+
MTR_LOG_INFO("Delete failed for endpoint index %@ for @ node 0x%016llX", endpointID, nodeID.unsignedLongLongValue);
741+
}
742+
return success;
743+
}
744+
745+
- (void)clearStoredClusterDataForNodeIDWithEndpointID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID
746+
{
747+
dispatch_async(_storageDelegateQueue, ^{
748+
BOOL success = [self _clearStoredClusterDataForNodeIDWithEndpointID:nodeID endpointID:endpointID];
749+
if (!success) {
750+
MTR_LOG_INFO("clearStoredClusterDataForNodeIDWithEndpointID: Delete failed for clusterData for @ node 0x%016llX endpoint %u", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue);
751+
}
752+
MTR_LOG_INFO("clearStoredClusterDataForNodeIDWithEndpointID: Successfully deleted cluster index and data for @ node 0x%016llX endpoint %u", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue);
753+
});
754+
}
755+
756+
- (void)clearStoredClusterDataForNodeIDWithClusterID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID
757+
{
758+
dispatch_async(_storageDelegateQueue, ^{
759+
BOOL success = [self _deleteClusterDataForNodeID:nodeID endpointID:endpointID clusterID:clusterID];
760+
if (!success) {
761+
MTR_LOG_INFO("clearStoredClusterDataForNodeIDWithClusterID: _deleteClusterDataForNodeID failed for @ node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, clusterID.unsignedLongValue);
762+
return;
763+
}
764+
765+
NSArray<NSNumber *> * clusterIndex = [self _fetchClusterIndexForNodeID:nodeID endpointID:endpointID];
766+
NSMutableArray<NSNumber *> * clusterIndexCopy = [clusterIndex mutableCopy];
767+
[clusterIndexCopy removeObject:clusterID];
768+
769+
if (clusterIndexCopy.count != clusterIndex.count) {
770+
success = [self _storeClusterIndex:clusterIndexCopy forNodeID:nodeID endpointID:endpointID];
771+
if (!success) {
772+
MTR_LOG_INFO("clearStoredClusterDataForNodeIDWithClusterID: _storeClusterIndex failed for @ node 0x%016llX endpoint %u", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue);
773+
}
774+
}
775+
MTR_LOG_INFO("clearStoredClusterDataForNodeIDWithClusterID: Deleted endpoint %u cluster 0x%08lX for @ node 0x%016llX successfully", endpointID.unsignedShortValue, clusterID.unsignedLongValue, nodeID.unsignedLongLongValue);
776+
});
777+
}
778+
702779
- (void)clearAllStoredClusterData
703780
{
704781
dispatch_async(_storageDelegateQueue, ^{

src/darwin/Framework/CHIP/MTRDevice_Internal.h

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ MTR_TESTABLE
3939
@property (nonatomic, readonly) NSDictionary<NSNumber *, MTRDeviceDataValueDictionary> * attributes; // attributeID => data-value dictionary
4040

4141
- (void)storeValue:(MTRDeviceDataValueDictionary _Nullable)value forAttribute:(NSNumber *)attribute;
42+
- (void)_removeValueForAttribute:(NSNumber *)attribute;
4243

4344
- (nullable instancetype)initWithDataVersion:(NSNumber * _Nullable)dataVersion attributes:(NSDictionary<NSNumber *, MTRDeviceDataValueDictionary> * _Nullable)attributes;
4445
@end

0 commit comments

Comments
 (0)