diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index 027aba35f46da0..a7aa7645185a7c 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -1508,15 +1508,15 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray * attributeReponseValue in reportedAttributeValues) { - MTRAttributePath * attributePath = attributeReponseValue[MTRAttributePathKey]; - NSDictionary * attributeDataValue = attributeReponseValue[MTRDataKey]; - NSError * attributeError = attributeReponseValue[MTRErrorKey]; + for (NSDictionary * attributeResponseValue in reportedAttributeValues) { + MTRAttributePath * attributePath = attributeResponseValue[MTRAttributePathKey]; + NSDictionary * attributeDataValue = attributeResponseValue[MTRDataKey]; + NSError * attributeError = attributeResponseValue[MTRErrorKey]; NSDictionary * previousValue; // sanity check either data value or error must exist if (!attributeDataValue && !attributeError) { - MTR_LOG_INFO("%@ report %@ no data value or error: %@", self, attributePath, attributeReponseValue); + MTR_LOG_INFO("%@ report %@ no data value or error: %@", self, attributePath, attributeResponseValue); continue; } @@ -1540,7 +1540,7 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray * attributeReponseValue in expectedAttributeValues) { - MTRAttributePath * attributePath = attributeReponseValue[MTRAttributePathKey]; - NSDictionary * attributeDataValue = attributeReponseValue[MTRDataKey]; + for (NSDictionary * attributeResponseValue in expectedAttributeValues) { + MTRAttributePath * attributePath = attributeResponseValue[MTRAttributePathKey]; + NSDictionary * attributeDataValue = attributeResponseValue[MTRDataKey]; BOOL shouldReportValue = NO; NSDictionary * attributeValueToReport; diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index e5bd6c60f02d97..0397268509ca76 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -853,7 +853,7 @@ - (MTRDevice *)deviceForNodeID:(NSNumber *)nodeID } // Load persisted attributes if they exist. - NSArray * attributesFromCache = [_controllerDataStore getAttributeCacheForNodeID:nodeID]; + NSArray * attributesFromCache = [_controllerDataStore getStoredAttributesForNodeID:nodeID]; if (attributesFromCache) { [deviceToReturn setAttributeValues:attributesFromCache reportChanges:NO]; } @@ -1238,21 +1238,21 @@ - (void)downloadLogFromNodeWithID:(NSNumber *)nodeID #pragma - Unit Test accessors for data store #ifdef DEBUG -- (nullable NSArray *)unitTest_dataStore_getAttributeCacheForNodeID:(NSNumber *)nodeID +- (nullable NSArray *)unitTest_dataStore_getStoredAttributesForNodeID:(NSNumber *)nodeID { - return [_controllerDataStore getAttributeCacheForNodeID:nodeID]; + return [_controllerDataStore getStoredAttributesForNodeID:nodeID]; } -- (void)unitTest_dataStore_storeInCacheAttributes:(NSArray *)dataValues forNodeID:(NSNumber *)nodeID +- (void)unitTest_dataStore_storeAttributeValues:(NSArray *)dataValues forNodeID:(NSNumber *)nodeID { - [_controllerDataStore storeInCacheAttributes:dataValues forNodeID:nodeID]; + [_controllerDataStore storeAttributeValues:dataValues forNodeID:nodeID]; } -- (void)unitTest_dataStore_clearAttributeCacheForNodeID:(NSNumber *)nodeID +- (void)unitTest_dataStore_clearStoredAttributesForNodeID:(NSNumber *)nodeID { - [_controllerDataStore clearAttributeCacheForNodeID:nodeID]; + [_controllerDataStore clearStoredAttributesForNodeID:nodeID]; } -- (void)unitTest_dataStore_clearAllAttributeCache +- (void)unitTest_dataStore_clearAllStoredAttributes { - [_controllerDataStore clearAllAttributeCache]; + [_controllerDataStore clearAllStoredAttributes]; } #endif diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.h b/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.h index 111006748471bc..8df10f63e1d27d 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.h +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.h @@ -63,49 +63,13 @@ NS_ASSUME_NONNULL_BEGIN - (CHIP_ERROR)storeLastLocallyUsedNOC:(MTRCertificateTLVBytes)noc; - (MTRCertificateTLVBytes _Nullable)fetchLastLocallyUsedNOC; -/** MTRDevice cache storage - * - * Per controller: - * NodeID index - * key: "nodeID index" - * value: list of nodeIDs - * EndpointID index - * key: " endpoints" - * value: list of endpoint IDs - * ClusterID index - * key: " clusters" - * value: list of cluster IDs - * AttributeID index - * key: " attributes" - * value: list of attribute IDs - * Attribute data: - * key: " attribute data" - * value: serialized dictionary of attribute data - * - * Attribute data dictionary - * Additional value "serial number" - */ -- (nullable NSArray *)getAttributeCacheForNodeID:(NSNumber *)nodeID; -- (void)storeInCacheAttributes:(NSArray *)dataValues forNodeID:(NSNumber *)nodeID; -- (void)clearAttributeCacheForNodeID:(NSNumber *)nodeID; -- (void)clearAllAttributeCache; - /** - * MTRDevice cache - * getAttributesToReportWithReportedValues - * Take updaes => send non-error paths and call store - * init - * Call getAttributeCacheForNodeID - * - * MTRDevieControllerDataStore - * init - * Read serial number and advance-jump / store - * - * MTRDevieControllerDataStore - * Need lock for store / get transactions to work without stomping each other - * - * TODO: Need to think about clean up for stale nodes + * Storage for MTRDevice attribute read cache. This is local-only storage as an optimization. New controller devices using MTRDevice API can prime their own local cache from devices directly. */ +- (nullable NSArray *)getStoredAttributesForNodeID:(NSNumber *)nodeID; +- (void)storeAttributeValues:(NSArray *)dataValues forNodeID:(NSNumber *)nodeID; +- (void)clearStoredAttributesForNodeID:(NSNumber *)nodeID; +- (void)clearAllStoredAttributes; @end diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.mm index f381be45912328..12acafb4db0097 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.mm @@ -16,6 +16,7 @@ #include "MTRDeviceControllerDataStore.h" +// Importing MTRBaseDevice.h for the MTRAttributePath class. Needs to change when https://github.com/project-chip/connectedhomeip/issues/31247 is fixed. #import "MTRBaseDevice.h" #import "MTRLogging_Internal.h" @@ -280,9 +281,6 @@ - (nullable MTRCASESessionResumptionInfo *)_findResumptionInfoWithKey:(nullable /** MTRDevice cache storage * * Per controller: - * Last serial number - * key: "last serial number" - * value: uint64 serial number * NodeID index * key: "attrCacheNodeIndex" * value: list of nodeIDs @@ -309,7 +307,7 @@ - (id)_fetchAttributeCacheValueForKey:(NSString *)key expectedClass:(Class)expec dispatch_sync(_storageDelegateQueue, ^{ data = [_storageDelegate controller:_controller valueForKey:key - securityLevel:MTRStorageSecurityLevelNotSecure + securityLevel:MTRStorageSecurityLevelSecure sharingType:MTRStorageSharingTypeNotShared]; }); @@ -324,25 +322,21 @@ - (id)_fetchAttributeCacheValueForKey:(NSString *)key expectedClass:(Class)expec return data; } -- (void)_storeAttributeCacheValue:(id)value forKey:(NSString *)key +- (BOOL)_storeAttributeCacheValue:(id)value forKey:(NSString *)key { - dispatch_sync(_storageDelegateQueue, ^{ - [_storageDelegate controller:_controller - storeValue:value - forKey:key - securityLevel:MTRStorageSecurityLevelNotSecure - sharingType:MTRStorageSharingTypeNotShared]; - }); + return [_storageDelegate controller:_controller + storeValue:value + forKey:key + securityLevel:MTRStorageSecurityLevelSecure + sharingType:MTRStorageSharingTypeNotShared]; } - (void)_removeAttributeCacheValueForKey:(NSString *)key { - dispatch_sync(_storageDelegateQueue, ^{ - [_storageDelegate controller:_controller - removeValueForKey:key - securityLevel:MTRStorageSecurityLevelNotSecure - sharingType:MTRStorageSharingTypeNotShared]; - }); + [_storageDelegate controller:_controller + removeValueForKey:key + securityLevel:MTRStorageSecurityLevelSecure + sharingType:MTRStorageSharingTypeNotShared]; } static NSString * sAttributeCacheNodeIndexKey = @"attrCacheNodeIndex"; @@ -352,9 +346,9 @@ - (void)_removeAttributeCacheValueForKey:(NSString *)key return [self _fetchAttributeCacheValueForKey:sAttributeCacheNodeIndexKey expectedClass:[NSArray class]]; } -- (void)_storeNodeIndex:(NSArray *)nodeIndex +- (BOOL)_storeNodeIndex:(NSArray *)nodeIndex { - [self _storeAttributeCacheValue:nodeIndex forKey:sAttributeCacheNodeIndexKey]; + return [self _storeAttributeCacheValue:nodeIndex forKey:sAttributeCacheNodeIndexKey]; } - (void)_deleteNodeIndex @@ -366,7 +360,7 @@ - (void)_deleteNodeIndex - (NSString *)_endpointIndexKeyForNodeID:(NSNumber *)nodeID { - return [sAttributeCacheEndpointIndexKeyPrefix stringByAppendingFormat:@":%@", nodeID]; + return [sAttributeCacheEndpointIndexKeyPrefix stringByAppendingFormat:@":0x%016llX", nodeID.unsignedLongLongValue]; } - (nullable NSArray *)_fetchEndpointIndexForNodeID:(NSNumber *)nodeID @@ -374,9 +368,9 @@ - (NSString *)_endpointIndexKeyForNodeID:(NSNumber *)nodeID return [self _fetchAttributeCacheValueForKey:[self _endpointIndexKeyForNodeID:nodeID] expectedClass:[NSArray class]]; } -- (void)_storeEndpointIndex:(NSArray *)endpointIndex forNodeID:(NSNumber *)nodeID +- (BOOL)_storeEndpointIndex:(NSArray *)endpointIndex forNodeID:(NSNumber *)nodeID { - [self _storeAttributeCacheValue:endpointIndex forKey:[self _endpointIndexKeyForNodeID:nodeID]]; + return [self _storeAttributeCacheValue:endpointIndex forKey:[self _endpointIndexKeyForNodeID:nodeID]]; } - (void)_deleteEndpointIndexForNodeID:(NSNumber *)nodeID @@ -388,7 +382,7 @@ - (void)_deleteEndpointIndexForNodeID:(NSNumber *)nodeID - (NSString *)_clusterIndexKeyForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID { - return [sAttributeCacheClusterIndexKeyPrefix stringByAppendingFormat:@":%@:%@", nodeID, endpointID]; + return [sAttributeCacheClusterIndexKeyPrefix stringByAppendingFormat:@":0x%016llX:%0x04X", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue]; } - (nullable NSArray *)_fetchClusterIndexForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID @@ -396,9 +390,9 @@ - (NSString *)_clusterIndexKeyForNodeID:(NSNumber *)nodeID endpointID:(NSNumber return [self _fetchAttributeCacheValueForKey:[self _clusterIndexKeyForNodeID:nodeID endpointID:endpointID] expectedClass:[NSArray class]]; } -- (void)_storeClusterIndex:(NSArray *)clusterIndex forNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID +- (BOOL)_storeClusterIndex:(NSArray *)clusterIndex forNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID { - [self _storeAttributeCacheValue:clusterIndex forKey:[self _clusterIndexKeyForNodeID:nodeID endpointID:endpointID]]; + return [self _storeAttributeCacheValue:clusterIndex forKey:[self _clusterIndexKeyForNodeID:nodeID endpointID:endpointID]]; } - (void)_deleteClusterIndexForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID @@ -410,7 +404,7 @@ - (void)_deleteClusterIndexForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)e - (NSString *)_attributeIndexKeyForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID { - return [sAttributeCacheAttributeIndexKeyPrefix stringByAppendingFormat:@":%@:%@:%@", nodeID, endpointID, clusterID]; + return [sAttributeCacheAttributeIndexKeyPrefix stringByAppendingFormat:@":0x%016llX:0x%04X:0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, clusterID.unsignedLongValue]; } - (nullable NSArray *)_fetchAttributeIndexForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID @@ -418,9 +412,9 @@ - (NSString *)_attributeIndexKeyForNodeID:(NSNumber *)nodeID endpointID:(NSNumbe return [self _fetchAttributeCacheValueForKey:[self _attributeIndexKeyForNodeID:nodeID endpointID:endpointID clusterID:clusterID] expectedClass:[NSArray class]]; } -- (void)_storeAttributeIndex:(NSArray *)attributeIndex forNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID +- (BOOL)_storeAttributeIndex:(NSArray *)attributeIndex forNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID { - [self _storeAttributeCacheValue:attributeIndex forKey:[self _attributeIndexKeyForNodeID:nodeID endpointID:endpointID clusterID:clusterID]]; + return [self _storeAttributeCacheValue:attributeIndex forKey:[self _attributeIndexKeyForNodeID:nodeID endpointID:endpointID clusterID:clusterID]]; } - (void)_deleteAttributeIndexForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID @@ -432,7 +426,7 @@ - (void)_deleteAttributeIndexForNodeID:(NSNumber *)nodeID endpointID:(NSNumber * - (NSString *)_attributeValueKeyForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID { - return [sAttributeCacheAttributeValueKeyPrefix stringByAppendingFormat:@":%@:%@:%@:%@", nodeID, endpointID, clusterID, attributeID]; + return [sAttributeCacheAttributeValueKeyPrefix stringByAppendingFormat:@":0x%016llX:0x%04X:0x%08lX:0x%08lX", nodeID.unsignedLongLongValue, endpointID.unsignedShortValue, clusterID.unsignedLongValue, attributeID.unsignedLongValue]; } - (nullable NSDictionary *)_fetchAttributeValueForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID @@ -440,9 +434,9 @@ - (nullable NSDictionary *)_fetchAttributeValueForNodeID:(NSNumber *)nodeID endp return [self _fetchAttributeCacheValueForKey:[self _attributeValueKeyForNodeID:nodeID endpointID:endpointID clusterID:clusterID attributeID:attributeID] expectedClass:[NSDictionary class]]; } -- (void)_storeAttributeValue:(NSDictionary *)value forNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID +- (BOOL)_storeAttributeValue:(NSDictionary *)value forNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID { - [self _storeAttributeCacheValue:value forKey:[self _attributeValueKeyForNodeID:nodeID endpointID:endpointID clusterID:clusterID attributeID:attributeID]]; + return [self _storeAttributeCacheValue:value forKey:[self _attributeValueKeyForNodeID:nodeID endpointID:endpointID clusterID:clusterID attributeID:attributeID]]; } - (void)_deleteAttributeValueForNodeID:(NSNumber *)nodeID endpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID @@ -452,7 +446,7 @@ - (void)_deleteAttributeValueForNodeID:(NSNumber *)nodeID endpointID:(NSNumber * #pragma - Attribute Cache management -- (nullable NSArray *)getAttributeCacheForNodeID:(NSNumber *)nodeID +- (nullable NSArray *)getStoredAttributesForNodeID:(NSNumber *)nodeID { NSMutableArray * attributesToReturn = nil; @@ -464,7 +458,7 @@ - (void)_deleteAttributeValueForNodeID:(NSNumber *)nodeID endpointID:(NSNumber * NSArray * endpointIndex = [self _fetchEndpointIndexForNodeID:nodeID]; if (endpointIndex) { MTR_LOG_ERROR("Persistent attribute cache contains orphaned entry for nodeID %@ - deleting", nodeID); - [self clearAttributeCacheForNodeID:nodeID]; + [self clearStoredAttributesForNodeID:nodeID]; } return nil; } @@ -501,67 +495,153 @@ - (void)_deleteAttributeValueForNodeID:(NSNumber *)nodeID endpointID:(NSNumber * return attributesToReturn; } -- (void)storeInCacheAttributes:(NSArray *)dataValues forNodeID:(NSNumber *)nodeID +- (void)_pruneEmptyStoredAttributesBranches { - for (NSDictionary * dataValue in dataValues) { - MTRAttributePath * path = dataValue[MTRAttributePathKey]; - NSDictionary * value = dataValue[MTRDataKey]; + // Fetch node index + NSMutableArray * nodeIndex = [self _fetchNodeIndex].mutableCopy; + NSUInteger nodeIndexCount = nodeIndex.count; - // Ensure node index exists - NSArray * nodeIndex = [self _fetchNodeIndex]; - if (!nodeIndex) { - nodeIndex = [NSArray arrayWithObject:nodeID]; - [self _storeNodeIndex:nodeIndex]; - } else if (![nodeIndex containsObject:nodeID]) { - [self _storeNodeIndex:[nodeIndex arrayByAddingObject:nodeID]]; + for (NSNumber * nodeID in nodeIndex) { + // Fetch endpoint index + NSMutableArray * endpointIndex = [self _fetchEndpointIndexForNodeID:nodeID].mutableCopy; + NSUInteger endpointIndexCount = endpointIndex.count; + + for (NSNumber * endpointID in endpointIndex) { + // Fetch endpoint index + NSMutableArray * clusterIndex = [self _fetchClusterIndexForNodeID:nodeID endpointID:endpointID].mutableCopy; + NSUInteger clusterIndexCount = clusterIndex.count; + + for (NSNumber * clusterID in clusterIndex) { + // Fetch endpoint index + NSMutableArray * attributeIndex = [self _fetchAttributeIndexForNodeID:nodeID endpointID:endpointID clusterID:clusterID].mutableCopy; + NSUInteger attributeIndexCount = attributeIndex.count; + + for (NSNumber * attributeID in attributeIndex) { + NSDictionary * value = [self _fetchAttributeValueForNodeID:nodeID endpointID:endpointID clusterID:clusterID attributeID:attributeID]; + + if (!value) { + [attributeIndex removeObject:attributeID]; + } + } + + if (!attributeIndex.count) { + [clusterIndex removeObject:clusterID]; + } else if (attributeIndex.count != attributeIndexCount) { + [self _storeAttributeIndex:attributeIndex forNodeID:nodeID endpointID:endpointID clusterID:clusterID]; + } + } + + if (!clusterIndex.count) { + [endpointIndex removeObject:endpointID]; + } else if (clusterIndex.count != clusterIndexCount) { + [self _storeClusterIndex:clusterIndex forNodeID:nodeID endpointID:endpointID]; + } } - // Ensure endpoint index exists - NSArray * endpointIndex = [self _fetchEndpointIndexForNodeID:nodeID]; - if (!endpointIndex) { - endpointIndex = [NSArray arrayWithObject:path.endpoint]; + if (!endpointIndex.count) { + [nodeIndex removeObject:nodeID]; + } else if (endpointIndex.count != endpointIndexCount) { [self _storeEndpointIndex:endpointIndex forNodeID:nodeID]; - } else if (![endpointIndex containsObject:path.endpoint]) { - [self _storeEndpointIndex:[endpointIndex arrayByAddingObject:path.endpoint] forNodeID:nodeID]; } + } - // Ensure cluster index exists - NSArray * clusterIndex = [self _fetchClusterIndexForNodeID:nodeID endpointID:path.endpoint]; - if (!clusterIndex) { - clusterIndex = [NSArray arrayWithObject:path.cluster]; - [self _storeClusterIndex:clusterIndex forNodeID:nodeID endpointID:path.endpoint]; - } else if (![clusterIndex containsObject:path.cluster]) { - [self _storeClusterIndex:[clusterIndex arrayByAddingObject:path.cluster] forNodeID:nodeID endpointID:path.endpoint]; - } + if (!nodeIndex.count) { + [self _deleteNodeIndex]; + } else if (nodeIndex.count != nodeIndexCount) { + [self _storeNodeIndex:nodeIndex]; + } +} + +- (void)storeAttributeValues:(NSArray *)dataValues forNodeID:(NSNumber *)nodeID +{ + dispatch_async(_storageDelegateQueue, ^{ + BOOL anyStoreFailed = NO; + + for (NSDictionary * dataValue in dataValues) { + MTRAttributePath * path = dataValue[MTRAttributePathKey]; + NSDictionary * value = dataValue[MTRDataKey]; + + BOOL storeFailed = NO; + // Ensure node index exists + NSArray * nodeIndex = [self _fetchNodeIndex]; + if (!nodeIndex) { + nodeIndex = [NSArray arrayWithObject:nodeID]; + storeFailed = ![self _storeNodeIndex:nodeIndex]; + } else if (![nodeIndex containsObject:nodeID]) { + storeFailed = ![self _storeNodeIndex:[nodeIndex arrayByAddingObject:nodeID]]; + } + if (storeFailed) { + anyStoreFailed = YES; + continue; + } + + // Ensure endpoint index exists + NSArray * endpointIndex = [self _fetchEndpointIndexForNodeID:nodeID]; + if (!endpointIndex) { + endpointIndex = [NSArray arrayWithObject:path.endpoint]; + storeFailed = ![self _storeEndpointIndex:endpointIndex forNodeID:nodeID]; + } else if (![endpointIndex containsObject:path.endpoint]) { + storeFailed = ![self _storeEndpointIndex:[endpointIndex arrayByAddingObject:path.endpoint] forNodeID:nodeID]; + } + if (storeFailed) { + anyStoreFailed = YES; + continue; + } - // TODO: Add per-cluster integrity check calculation and store with cluster - // TODO: Think about adding more integrity for + // Ensure cluster index exists + NSArray * clusterIndex = [self _fetchClusterIndexForNodeID:nodeID endpointID:path.endpoint]; + if (!clusterIndex) { + clusterIndex = [NSArray arrayWithObject:path.cluster]; + storeFailed = ![self _storeClusterIndex:clusterIndex forNodeID:nodeID endpointID:path.endpoint]; + } else if (![clusterIndex containsObject:path.cluster]) { + storeFailed = ![self _storeClusterIndex:[clusterIndex arrayByAddingObject:path.cluster] forNodeID:nodeID endpointID:path.endpoint]; + } + if (storeFailed) { + anyStoreFailed = YES; + continue; + } + + // TODO: Add per-cluster integrity check calculation and store with cluster + // TODO: Think about adding more integrity check for endpoint and node levels as well + + // Ensure attribute index exists + NSArray * attributeIndex = [self _fetchAttributeIndexForNodeID:nodeID endpointID:path.endpoint clusterID:path.cluster]; + if (!attributeIndex) { + attributeIndex = [NSArray arrayWithObject:path.attribute]; + storeFailed = ![self _storeAttributeIndex:attributeIndex forNodeID:nodeID endpointID:path.endpoint clusterID:path.cluster]; + } else if (![attributeIndex containsObject:path.attribute]) { + storeFailed = ![self _storeAttributeIndex:[attributeIndex arrayByAddingObject:path.attribute] forNodeID:nodeID endpointID:path.endpoint clusterID:path.cluster]; + } + if (storeFailed) { + anyStoreFailed = YES; + continue; + } - // Ensure attribute index exists - NSArray * attributeIndex = [self _fetchAttributeIndexForNodeID:nodeID endpointID:path.endpoint clusterID:path.cluster]; - if (!attributeIndex) { - attributeIndex = [NSArray arrayWithObject:path.attribute]; - [self _storeAttributeIndex:attributeIndex forNodeID:nodeID endpointID:path.endpoint clusterID:path.cluster]; - } else if (![attributeIndex containsObject:path.attribute]) { - [self _storeAttributeIndex:[attributeIndex arrayByAddingObject:path.attribute] forNodeID:nodeID endpointID:path.endpoint clusterID:path.cluster]; + // Store value + storeFailed = [self _storeAttributeValue:value forNodeID:nodeID endpointID:path.endpoint clusterID:path.cluster attributeID:path.attribute]; + if (storeFailed) { + anyStoreFailed = YES; + } } - // Store value - [self _storeAttributeValue:value forNodeID:nodeID endpointID:path.endpoint clusterID:path.cluster attributeID:path.attribute]; - } + // In the rare event that store fails, allow all attribute store attempts to go through and prune empty branches at the end altogether. + if (anyStoreFailed) { + [self _pruneEmptyStoredAttributesBranches]; + } + }); } -- (void)clearAttributeCacheForNodeID:(NSNumber *)nodeID +- (void)_clearStoredAttributesForNodeID:(NSNumber *)nodeID { // Fetch endpoint index NSArray * endpointIndex = [self _fetchEndpointIndexForNodeID:nodeID]; for (NSNumber * endpointID in endpointIndex) { - // Fetch endpoint index + // Fetch cluster index NSArray * clusterIndex = [self _fetchClusterIndexForNodeID:nodeID endpointID:endpointID]; for (NSNumber * clusterID in clusterIndex) { - // Fetch endpoint index + // Fetch attribute index NSArray * attributeIndex = [self _fetchAttributeIndexForNodeID:nodeID endpointID:endpointID clusterID:clusterID]; for (NSNumber * attributeID in attributeIndex) { @@ -577,16 +657,25 @@ - (void)clearAttributeCacheForNodeID:(NSNumber *)nodeID [self _deleteEndpointIndexForNodeID:nodeID]; } -- (void)clearAllAttributeCache +- (void)clearStoredAttributesForNodeID:(NSNumber *)nodeID { - // Fetch node index - NSArray * nodeIndex = [self _fetchNodeIndex]; + dispatch_async(_storageDelegateQueue, ^{ + [self _clearStoredAttributesForNodeID:nodeID]; + }); +} - for (NSNumber * nodeID in nodeIndex) { - [self clearAttributeCacheForNodeID:nodeID]; - } +- (void)clearAllStoredAttributes +{ + dispatch_async(_storageDelegateQueue, ^{ + // Fetch node index + NSArray * nodeIndex = [self _fetchNodeIndex]; - [self _deleteNodeIndex]; + for (NSNumber * nodeID in nodeIndex) { + [self _clearStoredAttributesForNodeID:nodeID]; + } + + [self _deleteNodeIndex]; + }); } @end diff --git a/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m b/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m index 12b9414155c0f5..c2c282d207df86 100644 --- a/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m +++ b/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m @@ -19,7 +19,6 @@ // system dependencies #import -#define CHIP_SYSTEM_CONFIG_USE_SOCKETS 1 #import "MTRDeviceTestDelegate.h" #import "MTRErrorTestUtils.h" #import "MTRFabricInfoChecker.h" @@ -37,10 +36,10 @@ // Declare unit test only methods to access data store directly for testing verification @interface MTRDeviceController (Test) - (void)removeDevice:(MTRDevice *)device; -- (nullable NSArray *)unitTest_dataStore_getAttributeCacheForNodeID:(NSNumber *)nodeID; -- (void)unitTest_dataStore_storeInCacheAttributes:(NSArray *)dataValues forNodeID:(NSNumber *)nodeID; -- (void)unitTest_dataStore_clearAttributeCacheForNodeID:(NSNumber *)nodeID; -- (void)unitTest_dataStore_clearAllAttributeCache; +- (nullable NSArray *)unitTest_dataStore_getStoredAttributesForNodeID:(NSNumber *)nodeID; +- (void)unitTest_dataStore_storeAttributeValues:(NSArray *)dataValues forNodeID:(NSNumber *)nodeID; +- (void)unitTest_dataStore_clearStoredAttributesForNodeID:(NSNumber *)nodeID; +- (void)unitTest_dataStore_clearAllStoredAttributes; @end @interface MTRDevice (Test) @@ -1079,7 +1078,6 @@ - (void)test008_TestDataStoreDirect XCTAssertEqualObjects(controller.controllerNodeID, nodeID); - // NSLog(@"JEFFTEST: dataStore %@", dataStore); NSArray * testAttributes = @[ @{ MTRAttributePathKey : [MTRAttributePath attributePathWithEndpointID:@(1) clusterID:@(1) attributeID:@(1)], MTRDataKey : @ { MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(111) } }, @{ MTRAttributePathKey : [MTRAttributePath attributePathWithEndpointID:@(1) clusterID:@(1) attributeID:@(2)], MTRDataKey : @ { MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(112) } }, @@ -1093,16 +1091,16 @@ - (void)test008_TestDataStoreDirect @{ MTRAttributePathKey : [MTRAttributePath attributePathWithEndpointID:@(2) clusterID:@(1) attributeID:@(2)], MTRDataKey : @ { MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(212) } }, @{ MTRAttributePathKey : [MTRAttributePath attributePathWithEndpointID:@(2) clusterID:@(1) attributeID:@(3)], MTRDataKey : @ { MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @(213) } }, ]; - [controller unitTest_dataStore_storeInCacheAttributes:testAttributes forNodeID:@(1001)]; - [controller unitTest_dataStore_storeInCacheAttributes:testAttributes forNodeID:@(1002)]; - [controller unitTest_dataStore_storeInCacheAttributes:testAttributes forNodeID:@(1003)]; + [controller unitTest_dataStore_storeAttributeValues:testAttributes forNodeID:@(1001)]; + [controller unitTest_dataStore_storeAttributeValues:testAttributes forNodeID:@(1002)]; + [controller unitTest_dataStore_storeAttributeValues:testAttributes forNodeID:@(1003)]; // Check values are written and can be fetched - NSArray * dataStoreValues = [controller unitTest_dataStore_getAttributeCacheForNodeID:@(1001)]; + NSArray * dataStoreValues = [controller unitTest_dataStore_getStoredAttributesForNodeID:@(1001)]; XCTAssertEqual(dataStoreValues.count, 9); - dataStoreValues = [controller unitTest_dataStore_getAttributeCacheForNodeID:@(1002)]; + dataStoreValues = [controller unitTest_dataStore_getStoredAttributesForNodeID:@(1002)]; XCTAssertEqual(dataStoreValues.count, 9); - dataStoreValues = [controller unitTest_dataStore_getAttributeCacheForNodeID:@(1003)]; + dataStoreValues = [controller unitTest_dataStore_getStoredAttributesForNodeID:@(1003)]; XCTAssertEqual(dataStoreValues.count, 9); // Check values @@ -1138,20 +1136,20 @@ - (void)test008_TestDataStoreDirect } } - [controller unitTest_dataStore_clearAttributeCacheForNodeID:@(1001)]; - dataStoreValues = [controller unitTest_dataStore_getAttributeCacheForNodeID:@(1001)]; + [controller unitTest_dataStore_clearStoredAttributesForNodeID:@(1001)]; + dataStoreValues = [controller unitTest_dataStore_getStoredAttributesForNodeID:@(1001)]; XCTAssertEqual(dataStoreValues.count, 0); - dataStoreValues = [controller unitTest_dataStore_getAttributeCacheForNodeID:@(1002)]; + dataStoreValues = [controller unitTest_dataStore_getStoredAttributesForNodeID:@(1002)]; XCTAssertEqual(dataStoreValues.count, 9); - dataStoreValues = [controller unitTest_dataStore_getAttributeCacheForNodeID:@(1003)]; + dataStoreValues = [controller unitTest_dataStore_getStoredAttributesForNodeID:@(1003)]; XCTAssertEqual(dataStoreValues.count, 9); - [controller unitTest_dataStore_clearAllAttributeCache]; - dataStoreValues = [controller unitTest_dataStore_getAttributeCacheForNodeID:@(1001)]; + [controller unitTest_dataStore_clearAllStoredAttributes]; + dataStoreValues = [controller unitTest_dataStore_getStoredAttributesForNodeID:@(1001)]; XCTAssertEqual(dataStoreValues.count, 0); - dataStoreValues = [controller unitTest_dataStore_getAttributeCacheForNodeID:@(1002)]; + dataStoreValues = [controller unitTest_dataStore_getStoredAttributesForNodeID:@(1002)]; XCTAssertEqual(dataStoreValues.count, 0); - dataStoreValues = [controller unitTest_dataStore_getAttributeCacheForNodeID:@(1003)]; + dataStoreValues = [controller unitTest_dataStore_getStoredAttributesForNodeID:@(1003)]; XCTAssertEqual(dataStoreValues.count, 0); [controller shutdown]; @@ -1213,7 +1211,7 @@ - (void)test009_TestDataStoreMTRDevice [self waitForExpectations:@[ subscriptionExpectation ] timeout:60]; - NSArray * dataStoreValues = [controller unitTest_dataStore_getAttributeCacheForNodeID:deviceID]; + NSArray * dataStoreValues = [controller unitTest_dataStore_getStoredAttributesForNodeID:deviceID]; // Verify all values are stored into storage for (NSDictionary * responseValue in dataStoreValues) { @@ -1226,7 +1224,7 @@ - (void)test009_TestDataStoreMTRDevice XCTAssertTrue([device _attributeDataValue:dataValue isEqualToDataValue:dataValueFromMTRDevice]); } - // Now force the removal of the object from + // Now force the removal of the object from controller to test reloading read cache from storage [controller removeDevice:device]; // Verify the new device is initialized with the same values @@ -1238,7 +1236,6 @@ - (void)test009_TestDataStoreMTRDevice XCTAssertNotNil(dataValue); NSDictionary * dataValueFromMTRDevice = [deviceNew readAttributeWithEndpointID:path.endpoint clusterID:path.cluster attributeID:path.attribute params:nil]; - NSLog(@"JEFFTEST: %@ =? %@", dataValue, dataValueFromMTRDevice); XCTAssertTrue([deviceNew _attributeDataValue:dataValue isEqualToDataValue:dataValueFromMTRDevice]); } diff --git a/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.m b/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.m index aedfbc9fc5b8c4..9e09aab6d96dd5 100644 --- a/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.m +++ b/src/darwin/Framework/CHIPTests/TestHelpers/MTRDeviceTestDelegate.m @@ -19,14 +19,10 @@ @implementation MTRDeviceTestDelegate - (void)device:(MTRDevice *)device stateChanged:(MTRDeviceState)state { - if (state == MTRDeviceStateReachable) { - if (self.onReachable != nil) { - self.onReachable(); - } + if (state == MTRDeviceStateReachable && self.onReachable != nil) { + self.onReachable(); } else if (state != MTRDeviceStateReachable && self.onNotReachable != nil) { - if (self.onNotReachable != nil) { - self.onNotReachable(); - } + self.onNotReachable(); } }