Skip to content

Commit 1b4c188

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 6bd80b0 commit 1b4c188

6 files changed

+1265
-7
lines changed

src/darwin/Framework/CHIP/MTRDevice.mm

+198-2
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ - (void)storeValue:(MTRDeviceDataValueDictionary _Nullable)value forAttribute:(N
220220
_attributes[attribute] = value;
221221
}
222222

223+
- (void)_removeValueForAttribute:(NSNumber *)attribute
224+
{
225+
[_attributes removeObjectForKey:attribute];
226+
}
227+
223228
- (NSDictionary<NSNumber *, MTRDeviceDataValueDictionary> *)attributes
224229
{
225230
return _attributes;
@@ -1609,6 +1614,18 @@ - (void)_setCachedAttributeValue:(MTRDeviceDataValueDictionary _Nullable)value f
16091614
_clusterDataToPersist[clusterPath] = clusterData;
16101615
}
16111616

1617+
- (void)_removeCachedAttributeValue:(MTRClusterPath *)clusterPath forPath:(MTRAttributePath *)attributePath
1618+
{
1619+
os_unfair_lock_assert_owner(&self->_lock);
1620+
1621+
if (_clusterDataToPersist == nil) {
1622+
return;
1623+
}
1624+
auto * clusterData = [_clusterDataToPersist objectForKey:clusterPath];
1625+
[clusterData _removeValueForAttribute:attributePath.attribute];
1626+
[_clusterDataToPersist setObject:clusterData forKey:clusterPath];
1627+
}
1628+
16121629
- (void)_createDataVersionFilterListFromDictionary:(NSDictionary<MTRClusterPath *, NSNumber *> *)dataVersions dataVersionFilterList:(DataVersionFilter **)dataVersionFilterList count:(size_t *)count sizeReduction:(size_t)sizeReduction
16131630
{
16141631
size_t maxDataVersionFilterSize = dataVersions.count;
@@ -2666,7 +2683,7 @@ - (BOOL)_attributeDataValue:(NSDictionary *)one isEqualToDataValue:(NSDictionary
26662683
}
26672684

26682685
// Utility to return data value dictionary without data version
2669-
- (NSDictionary *)_dataValueWithoutDataVersion:(NSDictionary *)attributeValue;
2686+
- (NSDictionary *)_dataValueWithoutDataVersion:(NSDictionary *)attributeValue
26702687
{
26712688
// Sanity check for nil - return the same input to fail gracefully
26722689
if (!attributeValue || !attributeValue[MTRTypeKey]) {
@@ -2728,6 +2745,137 @@ - (BOOL)_attributeAffectsDeviceConfiguration:(MTRAttributePath *)attributePath
27282745
return NO;
27292746
}
27302747

2748+
- (BOOL)_needsPruningOfEndpointsAndClusters:(MTRAttributePath *)attributePath
2749+
{
2750+
// Check for attributes in the descriptor cluster that could cause removal of endpoints and clusters.
2751+
if (attributePath.cluster.unsignedLongValue == MTRClusterIDTypeDescriptorID) {
2752+
switch (attributePath.attribute.unsignedLongValue) {
2753+
case MTRAttributeIDTypeClusterDescriptorAttributePartsListID:
2754+
case MTRAttributeIDTypeClusterDescriptorAttributeServerListID:
2755+
return YES;
2756+
}
2757+
}
2758+
2759+
// Check for global attribute - attribute list that could cause removal of attributes.
2760+
switch (attributePath.attribute.unsignedLongValue) {
2761+
case MTRAttributeIDTypeGlobalAttributeAttributeListID:
2762+
return YES;
2763+
}
2764+
return NO;
2765+
}
2766+
2767+
- (void)_pruneOrphanedEndpointsAndClusters:(MTRAttributePath *)attributePath
2768+
previousValue:(NSDictionary *)previousValue
2769+
attributeDataValue:(NSDictionary *)attributeDataValue
2770+
{
2771+
os_unfair_lock_assert_owner(&self->_lock);
2772+
2773+
if (_persistedClusters == nil || _persistedClusterData == nil || !previousValue.count)
2774+
{
2775+
return;
2776+
}
2777+
// Check if parts list changed or server list changed for the descriptor cluster or the attribute list changed for a cluster.
2778+
// If yes, we might need to prune any deleted endpoints, clusters or attributes from the storage and persisted cluster data.
2779+
if (attributePath.cluster.unsignedLongValue == MTRClusterIDTypeDescriptorID) {
2780+
switch (attributePath.attribute.unsignedLongValue) {
2781+
2782+
// If the parts list changed and one or more endpoints were removed, remove all the clusters in _persistedClusters and _persistedClusterData for all those endpoints.
2783+
// Also remove it from the data store.
2784+
case MTRAttributeIDTypeClusterDescriptorAttributePartsListID:
2785+
{
2786+
NSMutableSet * toBeRemovedEndpoints = [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:previousValue]];
2787+
NSSet * endpointsOnDevice = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:attributeDataValue]];
2788+
[toBeRemovedEndpoints minusSet:endpointsOnDevice];
2789+
2790+
for (NSNumber * endpoint in toBeRemovedEndpoints)
2791+
{
2792+
NSMutableSet<MTRClusterPath *> * clusterPathsToRemove = [[NSMutableSet alloc]init];
2793+
for (MTRClusterPath * path in _persistedClusters)
2794+
{
2795+
if ([path.endpoint isEqualToNumber:endpoint])
2796+
{
2797+
[clusterPathsToRemove addObject:path];
2798+
[_persistedClusterData removeObjectForKey:path];
2799+
[self.deviceController.controllerDataStore clearStoredClusterDataForNodeIDWithEndpointID:self.nodeID endpointID:endpoint];
2800+
}
2801+
}
2802+
[_persistedClusters minusSet:clusterPathsToRemove];
2803+
}
2804+
break;
2805+
}
2806+
2807+
// If the server list changed and clusters were removed, remove the clusters from the _persistedClusters and _persistedClusterData for that endpoint
2808+
// Also remove it from the data store.
2809+
case MTRAttributeIDTypeClusterDescriptorAttributeServerListID:
2810+
{
2811+
NSMutableSet * toBeRemovedClusters= [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:previousValue]];
2812+
NSSet * clustersOnDevice = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:attributeDataValue]];
2813+
[toBeRemovedClusters minusSet:clustersOnDevice];
2814+
2815+
NSMutableSet<MTRClusterPath *> * clusterPathsToRemove = [[NSMutableSet alloc]init];
2816+
for (NSNumber * cluster in toBeRemovedClusters)
2817+
{
2818+
for (MTRClusterPath * path in _persistedClusters)
2819+
{
2820+
if ([path.endpoint isEqualToNumber:attributePath.endpoint] && [path.cluster isEqualToNumber:cluster])
2821+
{
2822+
[clusterPathsToRemove addObject:path];
2823+
[_persistedClusterData removeObjectForKey:path];
2824+
2825+
[self.deviceController.controllerDataStore clearStoredClusterDataForNodeIDWithClusterID:self.nodeID endpointID:path.endpoint clusterID:path.cluster];
2826+
}
2827+
}
2828+
}
2829+
[_persistedClusters minusSet:clusterPathsToRemove];
2830+
break;
2831+
}
2832+
}
2833+
}
2834+
2835+
switch (attributePath.attribute.unsignedLongValue) {
2836+
// If the attribute list changed and attributes were removed, remove the attributes from the _persistedClusterData for that cluster and endpoint.
2837+
// Also remove it from the data store cluster data.
2838+
case MTRAttributeIDTypeGlobalAttributeAttributeListID:
2839+
{
2840+
NSMutableSet * toBeRemovedAttributes= [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:[self _cachedAttributeValueForPath:attributePath]]];
2841+
NSSet * attributesOnDevice = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:attributeDataValue]];
2842+
2843+
[toBeRemovedAttributes minusSet:attributesOnDevice];
2844+
for (NSNumber * attribute in toBeRemovedAttributes)
2845+
{
2846+
for (MTRClusterPath * path in _persistedClusters)
2847+
{
2848+
if ([path.endpoint isEqualToNumber:attributePath.endpoint] && [path.cluster isEqualToNumber:attributePath.cluster])
2849+
{
2850+
MTRDeviceClusterData * clusterData = [self _clusterDataForPath:path];
2851+
if (clusterData == nil)
2852+
{
2853+
return;
2854+
}
2855+
[clusterData _removeValueForAttribute:attribute];
2856+
[self->_persistedClusterData setObject:clusterData forKey:path];
2857+
2858+
NSDictionary<MTRClusterPath *, MTRDeviceClusterData *> * dataStoreClusterData = [self.deviceController.controllerDataStore getStoredClusterDataForNodeID:self.nodeID];
2859+
NSMutableDictionary<MTRClusterPath *, MTRDeviceClusterData *> * dataStoreClusterDataCopy = [dataStoreClusterData mutableCopy];
2860+
for (MTRClusterPath * dataStorePath in dataStoreClusterData)
2861+
{
2862+
if ([dataStorePath isEqualTo:path])
2863+
{
2864+
[dataStoreClusterDataCopy removeObjectForKey:path];
2865+
[dataStoreClusterDataCopy setObject:clusterData forKey:path];
2866+
[self.deviceController.controllerDataStore storeClusterData:dataStoreClusterDataCopy forNodeID:self.nodeID];
2867+
dataStoreClusterData = [NSMutableDictionary dictionaryWithDictionary:[self.deviceController.controllerDataStore getStoredClusterDataForNodeID:self.nodeID]];
2868+
}
2869+
}
2870+
[self _removeCachedAttributeValue:path forPath:attributePath];
2871+
}
2872+
}
2873+
}
2874+
break;
2875+
}
2876+
}
2877+
}
2878+
27312879
// assume lock is held
27322880
- (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSString *, id> *> *)reportedAttributeValues fromSubscription:(BOOL)isFromSubscription
27332881
{
@@ -2781,13 +2929,20 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
27812929
[self _noteDataVersion:dataVersion forClusterPath:clusterPath];
27822930
}
27832931

2784-
[self _setCachedAttributeValue:attributeDataValue forPath:attributePath fromSubscription:isFromSubscription];
2932+
if ([self _needsPruningOfEndpointsAndClusters:attributePath])
2933+
{
2934+
previousValue = [self _dataValueWithoutDataVersion:previousValue];
2935+
[self _pruneOrphanedEndpointsAndClusters:attributePath previousValue:previousValue attributeDataValue:attributeDataValue];
2936+
}
2937+
27852938
if (!_deviceConfigurationChanged) {
27862939
_deviceConfigurationChanged = [self _attributeAffectsDeviceConfiguration:attributePath];
27872940
if (_deviceConfigurationChanged) {
27882941
MTR_LOG_INFO("Device configuration changed due to changes in attribute %@", attributePath);
27892942
}
27902943
}
2944+
2945+
[self _setCachedAttributeValue:attributeDataValue forPath:attributePath fromSubscription:isFromSubscription];
27912946
}
27922947

27932948
#ifdef DEBUG
@@ -2941,6 +3096,47 @@ - (void)_storePersistedDeviceData
29413096
[datastore storeDeviceData:[data copy] forNodeID:self.nodeID];
29423097
}
29433098

3099+
- (void)_removePersistedClusterDataForPath:(MTRClusterPath *)path
3100+
{
3101+
os_unfair_lock_assert_owner(&self->_lock);
3102+
if (_persistedClusters == nil || _persistedClusterData == nil)
3103+
{
3104+
return;
3105+
}
3106+
3107+
[_persistedClusterData removeObjectForKey:path];
3108+
[_persistedClusters removeObject:path];
3109+
}
3110+
3111+
- (NSMutableSet<MTRClusterPath *> *)_getPersistedClusters
3112+
{
3113+
std::lock_guard lock(_lock);
3114+
3115+
return _persistedClusters;
3116+
}
3117+
3118+
- (MTRDeviceClusterData *)_getPersistedClusterDataForPath:(MTRClusterPath *)path
3119+
{
3120+
std::lock_guard lock(_lock);
3121+
3122+
if ([_persistedClusters containsObject:path])
3123+
{
3124+
return [_persistedClusterData objectForKey:path];
3125+
}
3126+
return nil;
3127+
}
3128+
3129+
- (BOOL)_persistedClusterContains:(MTRClusterPath *)path
3130+
{
3131+
std::lock_guard lock(_lock);
3132+
3133+
if ([_persistedClusters containsObject:path])
3134+
{
3135+
return YES;
3136+
}
3137+
return NO;
3138+
}
3139+
29443140
- (BOOL)deviceCachePrimed
29453141
{
29463142
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
/**

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)