@@ -209,6 +209,11 @@ - (void)storeValue:(MTRDeviceDataValueDictionary _Nullable)value forAttribute:(N
209
209
_attributes[attribute] = value;
210
210
}
211
211
212
+ - (void)removeValueForAttribute:(NSNumber *)attribute
213
+ {
214
+ [_attributes removeObjectForKey:attribute];
215
+ }
216
+
212
217
- (NSDictionary<NSNumber *, MTRDeviceDataValueDictionary> *)attributes
213
218
{
214
219
return _attributes;
@@ -670,7 +675,7 @@ - (void)_setDSTOffsets:(NSArray<MTRTimeSynchronizationClusterDSTOffsetStruct *>
670
675
completion:setDSTOffsetResponseHandler];
671
676
}
672
677
673
- - (NSMutableArray <NSNumber *> *)arrayOfNumbersFromAttributeValue : (NSDictionary * )dataDictionary
678
+ - (NSMutableArray<NSNumber *> *)arrayOfNumbersFromAttributeValue:(MTRDeviceDataValueDictionary )dataDictionary
674
679
{
675
680
if (![MTRArrayValueType isEqual:dataDictionary[MTRTypeKey]]) {
676
681
return nil;
@@ -1892,6 +1897,17 @@ - (void)_setCachedAttributeValue:(MTRDeviceDataValueDictionary _Nullable)value f
1892
1897
_clusterDataToPersist[clusterPath] = clusterData;
1893
1898
}
1894
1899
1900
+ - (void)_removeCachedAttribute:(NSNumber *)attributeID fromCluster:(MTRClusterPath *)clusterPath
1901
+ {
1902
+ os_unfair_lock_assert_owner(&self->_lock);
1903
+
1904
+ if (_clusterDataToPersist == nil) {
1905
+ return;
1906
+ }
1907
+ auto * clusterData = _clusterDataToPersist[clusterPath];
1908
+ [clusterData removeValueForAttribute:attributeID];
1909
+ }
1910
+
1895
1911
- (void)_createDataVersionFilterListFromDictionary:(NSDictionary<MTRClusterPath *, NSNumber *> *)dataVersions dataVersionFilterList:(DataVersionFilter **)dataVersionFilterList count:(size_t *)count sizeReduction:(size_t)sizeReduction
1896
1912
{
1897
1913
size_t maxDataVersionFilterSize = dataVersions.count;
@@ -2956,7 +2972,7 @@ - (BOOL)_attributeDataValue:(NSDictionary *)one isEqualToDataValue:(NSDictionary
2956
2972
}
2957
2973
2958
2974
// Utility to return data value dictionary without data version
2959
- - (NSDictionary *)_dataValueWithoutDataVersion : (NSDictionary *)attributeValue ;
2975
+ - (NSDictionary *)_dataValueWithoutDataVersion:(NSDictionary *)attributeValue
2960
2976
{
2961
2977
// Sanity check for nil - return the same input to fail gracefully
2962
2978
if (!attributeValue || !attributeValue[MTRTypeKey]) {
@@ -3018,6 +3034,116 @@ - (BOOL)_attributeAffectsDeviceConfiguration:(MTRAttributePath *)attributePath
3018
3034
return NO;
3019
3035
}
3020
3036
3037
+ - (void)_removeClusters:(NSSet<MTRClusterPath *> *)clusterPathsToRemove
3038
+ doRemoveFromDataStore:(BOOL)doRemoveFromDataStore
3039
+ {
3040
+ os_unfair_lock_assert_owner(&self->_lock);
3041
+
3042
+ [_persistedClusters minusSet:clusterPathsToRemove];
3043
+
3044
+ for (MTRClusterPath * path in clusterPathsToRemove) {
3045
+ [_persistedClusterData removeObjectForKey:path];
3046
+ [_clusterDataToPersist removeObjectForKey:path];
3047
+ if (doRemoveFromDataStore) {
3048
+ [self.deviceController.controllerDataStore clearStoredClusterDataForNodeID:self.nodeID endpointID:path.endpoint clusterID:path.cluster];
3049
+ }
3050
+ }
3051
+ }
3052
+
3053
+ - (void)_removeAttributes:(NSSet<NSNumber *> *)attributes fromCluster:(MTRClusterPath *)clusterPath
3054
+ {
3055
+ os_unfair_lock_assert_owner(&self->_lock);
3056
+
3057
+ for (NSNumber * attribute in attributes) {
3058
+ [self _removeCachedAttribute:attribute fromCluster:clusterPath];
3059
+ }
3060
+ // Just clear out the NSCache entry for this cluster, so we'll load it from storage as needed.
3061
+ [_persistedClusterData removeObjectForKey:clusterPath];
3062
+ [self.deviceController.controllerDataStore removeAttributes:attributes fromCluster:clusterPath forNodeID:self.nodeID];
3063
+ }
3064
+
3065
+ - (void)_pruneEndpointsIn:(MTRDeviceDataValueDictionary)previousPartsListValue
3066
+ missingFrom:(MTRDeviceDataValueDictionary)newPartsListValue
3067
+ {
3068
+ // If the parts list changed and one or more endpoints were removed, remove all the
3069
+ // clusters for all those endpoints from our data structures.
3070
+ // Also remove those endpoints from the data store.
3071
+ NSMutableSet<NSNumber *> * toBeRemovedEndpoints = [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:previousPartsListValue]];
3072
+ NSSet<NSNumber *> * endpointsOnDevice = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:newPartsListValue]];
3073
+ [toBeRemovedEndpoints minusSet:endpointsOnDevice];
3074
+
3075
+ for (NSNumber * endpoint in toBeRemovedEndpoints) {
3076
+ NSMutableSet<MTRClusterPath *> * clusterPathsToRemove = [[NSMutableSet alloc] init];
3077
+ for (MTRClusterPath * path in _persistedClusters) {
3078
+ if ([path.endpoint isEqualToNumber:endpoint]) {
3079
+ [clusterPathsToRemove addObject:path];
3080
+ }
3081
+ }
3082
+ [self _removeClusters:clusterPathsToRemove doRemoveFromDataStore:NO];
3083
+ [self.deviceController.controllerDataStore clearStoredClusterDataForNodeID:self.nodeID endpointID:endpoint];
3084
+ }
3085
+ }
3086
+
3087
+ - (void)_pruneClustersIn:(MTRDeviceDataValueDictionary)previousServerListValue
3088
+ missingFrom:(MTRDeviceDataValueDictionary)newServerListValue
3089
+ forEndpoint:(NSNumber *)endpointID
3090
+ {
3091
+ // If the server list changed and clusters were removed, remove those clusters from our data structures.
3092
+ // Also remove them from the data store.
3093
+ NSMutableSet<NSNumber *> * toBeRemovedClusters = [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:previousServerListValue]];
3094
+ NSSet<NSNumber *> * clustersStillOnEndpoint = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:newServerListValue]];
3095
+ [toBeRemovedClusters minusSet:clustersStillOnEndpoint];
3096
+
3097
+ NSMutableSet<MTRClusterPath *> * clusterPathsToRemove = [[NSMutableSet alloc] init];
3098
+ for (MTRClusterPath * path in _persistedClusters) {
3099
+ if ([path.endpoint isEqualToNumber:endpointID] && [toBeRemovedClusters containsObject:path.cluster])
3100
+ [clusterPathsToRemove addObject:path];
3101
+ }
3102
+ [self _removeClusters:clusterPathsToRemove doRemoveFromDataStore:YES];
3103
+ }
3104
+
3105
+ - (void)_pruneAttributesIn:(MTRDeviceDataValueDictionary)previousAttributeListValue
3106
+ missingFrom:(MTRDeviceDataValueDictionary)newAttributeListValue
3107
+ forCluster:(MTRClusterPath *)clusterPath
3108
+ {
3109
+ // If the attribute list changed and attributes were removed, remove the attributes from our
3110
+ // data structures.
3111
+ NSMutableSet<NSNumber *> * toBeRemovedAttributes = [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:previousAttributeListValue]];
3112
+ NSSet<NSNumber *> * attributesStillInCluster = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:newAttributeListValue]];
3113
+
3114
+ [toBeRemovedAttributes minusSet:attributesStillInCluster];
3115
+ [self _removeAttributes:toBeRemovedAttributes fromCluster:clusterPath];
3116
+ }
3117
+
3118
+ - (void)_pruneStoredDataForPath:(MTRAttributePath *)attributePath
3119
+ missingFrom:(MTRDeviceDataValueDictionary)newAttributeDataValue
3120
+ {
3121
+ os_unfair_lock_assert_owner(&self->_lock);
3122
+
3123
+ if (![self _dataStoreExists] && !_clusterDataToPersist.count) {
3124
+ MTR_LOG_DEBUG("%@ No data store to prune from", self);
3125
+ return;
3126
+ }
3127
+
3128
+ // Check if parts list changed or server list changed for the descriptor cluster or the attribute list changed for a cluster.
3129
+ // If yes, we might need to prune any deleted endpoints, clusters or attributes from the storage and persisted cluster data.
3130
+ if (attributePath.cluster.unsignedLongValue == MTRClusterIDTypeDescriptorID) {
3131
+ if (attributePath.attribute.unsignedLongValue == MTRAttributeIDTypeClusterDescriptorAttributePartsListID && [attributePath.endpoint isEqualToNumber:@(kRootEndpointId)]) {
3132
+ [self _pruneEndpointsIn:[self _cachedAttributeValueForPath:attributePath] missingFrom:newAttributeDataValue];
3133
+ return;
3134
+ }
3135
+
3136
+ if (attributePath.attribute.unsignedLongValue == MTRAttributeIDTypeClusterDescriptorAttributeServerListID) {
3137
+ [self _pruneClustersIn:[self _cachedAttributeValueForPath:attributePath] missingFrom:newAttributeDataValue forEndpoint:attributePath.endpoint];
3138
+ return;
3139
+ }
3140
+ }
3141
+
3142
+ if (attributePath.attribute.unsignedLongValue == MTRAttributeIDTypeGlobalAttributeAttributeListID) {
3143
+ [self _pruneAttributesIn:[self _cachedAttributeValueForPath:attributePath] missingFrom:newAttributeDataValue forCluster:[MTRClusterPath clusterPathWithEndpointID:attributePath.endpoint clusterID:attributePath.cluster]];
3144
+ }
3145
+ }
3146
+
3021
3147
// assume lock is held
3022
3148
- (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSString *, id> *> *)reportedAttributeValues fromSubscription:(BOOL)isFromSubscription
3023
3149
{
@@ -3071,13 +3197,16 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
3071
3197
[self _noteDataVersion:dataVersion forClusterPath:clusterPath];
3072
3198
}
3073
3199
3074
- [self _setCachedAttributeValue: attributeDataValue forPath: attributePath fromSubscription: isFromSubscription];
3200
+ [self _pruneStoredDataForPath:attributePath missingFrom:attributeDataValue];
3201
+
3075
3202
if (!_deviceConfigurationChanged) {
3076
3203
_deviceConfigurationChanged = [self _attributeAffectsDeviceConfiguration:attributePath];
3077
3204
if (_deviceConfigurationChanged) {
3078
3205
MTR_LOG("Device configuration changed due to changes in attribute %@", attributePath);
3079
3206
}
3080
3207
}
3208
+
3209
+ [self _setCachedAttributeValue:attributeDataValue forPath:attributePath fromSubscription:isFromSubscription];
3081
3210
}
3082
3211
3083
3212
#ifdef DEBUG
@@ -3230,6 +3359,22 @@ - (void)_storePersistedDeviceData
3230
3359
[datastore storeDeviceData:[data copy] forNodeID:self.nodeID];
3231
3360
}
3232
3361
3362
+ #ifdef DEBUG
3363
+ - (MTRDeviceClusterData *)_getClusterDataForPath:(MTRClusterPath *)path
3364
+ {
3365
+ std::lock_guard lock(_lock);
3366
+
3367
+ return [[self _clusterDataForPath:path] copy];
3368
+ }
3369
+
3370
+ - (BOOL)_clusterHasBeenPersisted:(MTRClusterPath *)path
3371
+ {
3372
+ std::lock_guard lock(_lock);
3373
+
3374
+ return [_persistedClusters containsObject:path];
3375
+ }
3376
+ #endif
3377
+
3233
3378
- (BOOL)deviceCachePrimed
3234
3379
{
3235
3380
std::lock_guard lock(_lock);
0 commit comments