Skip to content

Commit c1e1e6e

Browse files
nivi-applerestyled-commitsbzbarsky-applewoody-apple
authored
Add support to intercept attribute report and prune any removed… (#33523)
* 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. * Restyled by whitespace * Restyled by clang-format * fixes * Fix the check for the cluster paths * Apply suggestions from code review Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Address review comments * Restyled by whitespace * Restyled by clang-format * Added helpers for removing endpoints, clusters and attributes * Restyled by clang-format * Clean up the tests * Remove addition of extra line * Restyled by clang-format * Apply suggestions from code review Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Addressed review comments * Restyled by whitespace * Uncomment subscription pool tests * More clean up * Restyled by clang-format * Add few more strong types for data structures * Apply suggestions from code review Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Restyled by whitespace * Restyled by clang-format * Update src/darwin/Framework/CHIP/MTRDevice.mm --------- Co-authored-by: Restyled.io <commits@restyled.io> Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> Co-authored-by: Justin Wood <woody@apple.com>
1 parent a23560d commit c1e1e6e

6 files changed

+728
-3
lines changed

src/darwin/Framework/CHIP/MTRDevice.mm

+148-3
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ - (void)storeValue:(MTRDeviceDataValueDictionary _Nullable)value forAttribute:(N
209209
_attributes[attribute] = value;
210210
}
211211

212+
- (void)removeValueForAttribute:(NSNumber *)attribute
213+
{
214+
[_attributes removeObjectForKey:attribute];
215+
}
216+
212217
- (NSDictionary<NSNumber *, MTRDeviceDataValueDictionary> *)attributes
213218
{
214219
return _attributes;
@@ -670,7 +675,7 @@ - (void)_setDSTOffsets:(NSArray<MTRTimeSynchronizationClusterDSTOffsetStruct *>
670675
completion:setDSTOffsetResponseHandler];
671676
}
672677

673-
- (NSMutableArray<NSNumber *> *)arrayOfNumbersFromAttributeValue:(NSDictionary *)dataDictionary
678+
- (NSMutableArray<NSNumber *> *)arrayOfNumbersFromAttributeValue:(MTRDeviceDataValueDictionary)dataDictionary
674679
{
675680
if (![MTRArrayValueType isEqual:dataDictionary[MTRTypeKey]]) {
676681
return nil;
@@ -1892,6 +1897,17 @@ - (void)_setCachedAttributeValue:(MTRDeviceDataValueDictionary _Nullable)value f
18921897
_clusterDataToPersist[clusterPath] = clusterData;
18931898
}
18941899

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+
18951911
- (void)_createDataVersionFilterListFromDictionary:(NSDictionary<MTRClusterPath *, NSNumber *> *)dataVersions dataVersionFilterList:(DataVersionFilter **)dataVersionFilterList count:(size_t *)count sizeReduction:(size_t)sizeReduction
18961912
{
18971913
size_t maxDataVersionFilterSize = dataVersions.count;
@@ -2956,7 +2972,7 @@ - (BOOL)_attributeDataValue:(NSDictionary *)one isEqualToDataValue:(NSDictionary
29562972
}
29572973

29582974
// Utility to return data value dictionary without data version
2959-
- (NSDictionary *)_dataValueWithoutDataVersion:(NSDictionary *)attributeValue;
2975+
- (NSDictionary *)_dataValueWithoutDataVersion:(NSDictionary *)attributeValue
29602976
{
29612977
// Sanity check for nil - return the same input to fail gracefully
29622978
if (!attributeValue || !attributeValue[MTRTypeKey]) {
@@ -3018,6 +3034,116 @@ - (BOOL)_attributeAffectsDeviceConfiguration:(MTRAttributePath *)attributePath
30183034
return NO;
30193035
}
30203036

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+
30213147
// assume lock is held
30223148
- (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSString *, id> *> *)reportedAttributeValues fromSubscription:(BOOL)isFromSubscription
30233149
{
@@ -3071,13 +3197,16 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
30713197
[self _noteDataVersion:dataVersion forClusterPath:clusterPath];
30723198
}
30733199

3074-
[self _setCachedAttributeValue:attributeDataValue forPath:attributePath fromSubscription:isFromSubscription];
3200+
[self _pruneStoredDataForPath:attributePath missingFrom:attributeDataValue];
3201+
30753202
if (!_deviceConfigurationChanged) {
30763203
_deviceConfigurationChanged = [self _attributeAffectsDeviceConfiguration:attributePath];
30773204
if (_deviceConfigurationChanged) {
30783205
MTR_LOG("Device configuration changed due to changes in attribute %@", attributePath);
30793206
}
30803207
}
3208+
3209+
[self _setCachedAttributeValue:attributeDataValue forPath:attributePath fromSubscription:isFromSubscription];
30813210
}
30823211

30833212
#ifdef DEBUG
@@ -3230,6 +3359,22 @@ - (void)_storePersistedDeviceData
32303359
[datastore storeDeviceData:[data copy] forNodeID:self.nodeID];
32313360
}
32323361

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+
32333378
- (BOOL)deviceCachePrimed
32343379
{
32353380
std::lock_guard lock(_lock);

src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.h

+3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ 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)clearStoredClusterDataForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID;
80+
- (void)clearStoredClusterDataForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID;
81+
- (void)removeAttributes:(NSSet<NSNumber *> *)attributes fromCluster:(MTRClusterPath *)path forNodeID:(NSNumber *)nodeID;
7982
- (void)clearAllStoredClusterData;
8083

8184
/**

src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.mm

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

449+
- (BOOL)_removeEndpointFromEndpointIndex:(NSNumber *)endpointID forNodeID:(NSNumber *)nodeID
450+
{
451+
dispatch_assert_queue(_storageDelegateQueue);
452+
if (!endpointID || !nodeID) {
453+
MTR_LOG_ERROR("%s: unexpected nil input", __func__);
454+
return NO;
455+
}
456+
457+
NSMutableArray<NSNumber *> * endpointIndex = [[self _fetchEndpointIndexForNodeID:nodeID] mutableCopy];
458+
if (endpointIndex == nil) {
459+
return NO;
460+
}
461+
462+
[endpointIndex removeObject:endpointID];
463+
return [self _storeEndpointIndex:endpointIndex forNodeID:nodeID];
464+
}
465+
449466
- (BOOL)_deleteEndpointIndexForNodeID:(NSNumber *)nodeID
450467
{
451468
dispatch_assert_queue(_storageDelegateQueue);
@@ -699,6 +716,76 @@ - (void)clearStoredClusterDataForNodeID:(NSNumber *)nodeID
699716
});
700717
}
701718

719+
- (void)clearStoredClusterDataForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID
720+
{
721+
dispatch_async(_storageDelegateQueue, ^{
722+
NSArray<NSNumber *> * clusterIndex = [self _fetchClusterIndexForNodeID:nodeID endpointID:endpointID];
723+
NSMutableArray<NSNumber *> * clusterIndexCopy = [clusterIndex mutableCopy];
724+
[clusterIndexCopy removeObject:clusterID];
725+
726+
BOOL success;
727+
if (clusterIndexCopy.count != clusterIndex.count) {
728+
success = [self _storeClusterIndex:clusterIndexCopy forNodeID:nodeID endpointID:endpointID];
729+
if (!success) {
730+
MTR_LOG_ERROR("clearStoredClusterDataForNodeID: _storeClusterIndex failed for node 0x%016llX endpoint %u", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue);
731+
}
732+
}
733+
734+
success = [self _deleteClusterDataForNodeID:nodeID endpointID:endpointID clusterID:clusterID];
735+
if (!success) {
736+
MTR_LOG_ERROR("clearStoredClusterDataForNodeID: _deleteClusterDataForNodeID failed for node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, clusterID.unsignedLongValue);
737+
return;
738+
}
739+
740+
MTR_LOG("clearStoredClusterDataForNodeID: Deleted endpoint %u cluster 0x%08lX for node 0x%016llX successfully", endpointID.unsignedShortValue, clusterID.unsignedLongValue, nodeID.unsignedLongLongValue);
741+
});
742+
}
743+
744+
- (void)clearStoredClusterDataForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID
745+
{
746+
dispatch_async(_storageDelegateQueue, ^{
747+
BOOL success = [self _removeEndpointFromEndpointIndex:endpointID forNodeID:nodeID];
748+
if (!success) {
749+
MTR_LOG_ERROR("removeEndpointFromEndpointIndex for endpointID %u failed for node 0x%016llX", endpointID.unsignedShortValue, nodeID.unsignedLongLongValue);
750+
}
751+
752+
NSArray<NSNumber *> * clusterIndex = [self _fetchClusterIndexForNodeID:nodeID endpointID:endpointID];
753+
754+
for (NSNumber * cluster in clusterIndex) {
755+
success = [self _deleteClusterDataForNodeID:nodeID endpointID:endpointID clusterID:cluster];
756+
if (!success) {
757+
MTR_LOG_ERROR("Delete failed for clusterData for node 0x%016llX endpoint %u cluster 0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, cluster.unsignedLongValue);
758+
}
759+
}
760+
761+
success = [self _deleteClusterIndexForNodeID:nodeID endpointID:endpointID];
762+
if (!success) {
763+
MTR_LOG_ERROR("Delete failed for clusterIndex for node 0x%016llX endpoint %u", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue);
764+
}
765+
766+
MTR_LOG("clearStoredClusterDataForNodeID: Deleted endpoint %u for node 0x%016llX successfully", endpointID.unsignedShortValue, nodeID.unsignedLongLongValue);
767+
});
768+
}
769+
770+
- (void)removeAttributes:(NSSet<NSNumber *> *)attributes fromCluster:(MTRClusterPath *)path forNodeID:(NSNumber *)nodeID
771+
{
772+
MTRDeviceClusterData * clusterData = [self getStoredClusterDataForNodeID:nodeID endpointID:path.endpoint clusterID:path.cluster];
773+
if (clusterData == nil) {
774+
return;
775+
}
776+
for (NSNumber * attribute in attributes) {
777+
[clusterData removeValueForAttribute:attribute];
778+
}
779+
780+
dispatch_async(_storageDelegateQueue, ^{
781+
BOOL success = [self _storeClusterData:clusterData forNodeID:nodeID endpointID:path.endpoint clusterID:path.cluster];
782+
if (!success) {
783+
MTR_LOG_ERROR("removeAttributes: _storeClusterData failed for node 0x%016llX endpoint %u", nodeID.unsignedLongLongValue, path.endpoint.unsignedShortValue);
784+
}
785+
MTR_LOG("removeAttributes: Deleted attributes %@ from endpoint %u cluster 0x%08lX for node 0x%016llX successfully", attributes, path.endpoint.unsignedShortValue, path.cluster.unsignedLongValue, nodeID.unsignedLongLongValue);
786+
});
787+
}
788+
702789
- (void)clearAllStoredClusterData
703790
{
704791
dispatch_async(_storageDelegateQueue, ^{

src/darwin/Framework/CHIP/MTRDevice_Internal.h

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

6262
- (void)storeValue:(MTRDeviceDataValueDictionary _Nullable)value forAttribute:(NSNumber *)attribute;
63+
- (void)removeValueForAttribute:(NSNumber *)attribute;
6364

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

0 commit comments

Comments
 (0)