Skip to content

Commit 5a44923

Browse files
[Darwin] MTRDevice cache make use of controller storage for persistent cache (project-chip#31952)
* [Darwin] MTRDevice cache make use of controller storage for persistent cache --------- Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
1 parent bf46b08 commit 5a44923

12 files changed

+853
-66
lines changed

src/darwin/Framework/CHIP/MTRBaseDevice.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1))
549549
* wildcards).
550550
*/
551551
MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4))
552-
@interface MTRClusterPath : NSObject <NSCopying>
552+
@interface MTRClusterPath : NSObject <NSCopying, NSSecureCoding>
553553

554554
@property (nonatomic, readonly, copy) NSNumber * endpoint;
555555
@property (nonatomic, readonly, copy) NSNumber * cluster;
@@ -565,7 +565,7 @@ MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4))
565565
* wildcards).
566566
*/
567567
MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1))
568-
@interface MTRAttributePath : MTRClusterPath
568+
@interface MTRAttributePath : MTRClusterPath <NSSecureCoding>
569569

570570
@property (nonatomic, readonly, copy) NSNumber * attribute;
571571

src/darwin/Framework/CHIP/MTRBaseDevice.mm

+65
Original file line numberDiff line numberDiff line change
@@ -2425,6 +2425,42 @@ - (id)copyWithZone:(NSZone *)zone
24252425
return [MTRClusterPath clusterPathWithEndpointID:_endpoint clusterID:_cluster];
24262426
}
24272427

2428+
static NSString * const sEndpointKey = @"endpointKey";
2429+
static NSString * const sClusterKey = @"clusterKey";
2430+
2431+
+ (BOOL)supportsSecureCoding
2432+
{
2433+
return YES;
2434+
}
2435+
2436+
- (nullable instancetype)initWithCoder:(NSCoder *)decoder
2437+
{
2438+
self = [super init];
2439+
if (self == nil) {
2440+
return nil;
2441+
}
2442+
2443+
_endpoint = [decoder decodeObjectOfClass:[NSNumber class] forKey:sEndpointKey];
2444+
if (_endpoint && ![_endpoint isKindOfClass:[NSNumber class]]) {
2445+
MTR_LOG_ERROR("MTRClusterPath decoded %@ for endpoint, not NSNumber.", _endpoint);
2446+
return nil;
2447+
}
2448+
2449+
_cluster = [decoder decodeObjectOfClass:[NSNumber class] forKey:sClusterKey];
2450+
if (_cluster && ![_cluster isKindOfClass:[NSNumber class]]) {
2451+
MTR_LOG_ERROR("MTRClusterPath decoded %@ for cluster, not NSNumber.", _cluster);
2452+
return nil;
2453+
}
2454+
2455+
return self;
2456+
}
2457+
2458+
- (void)encodeWithCoder:(NSCoder *)coder
2459+
{
2460+
[coder encodeObject:_endpoint forKey:sEndpointKey];
2461+
[coder encodeObject:_cluster forKey:sClusterKey];
2462+
}
2463+
24282464
@end
24292465

24302466
@implementation MTRAttributePath
@@ -2482,6 +2518,35 @@ - (ConcreteAttributePath)_asConcretePath
24822518
return ConcreteAttributePath([self.endpoint unsignedShortValue], static_cast<ClusterId>([self.cluster unsignedLongValue]),
24832519
static_cast<AttributeId>([self.attribute unsignedLongValue]));
24842520
}
2521+
2522+
static NSString * const sAttributeKey = @"attributeKey";
2523+
2524+
+ (BOOL)supportsSecureCoding
2525+
{
2526+
return YES;
2527+
}
2528+
2529+
- (nullable instancetype)initWithCoder:(NSCoder *)decoder
2530+
{
2531+
self = [super initWithCoder:decoder];
2532+
if (self == nil) {
2533+
return nil;
2534+
}
2535+
2536+
_attribute = [decoder decodeObjectOfClass:[NSNumber class] forKey:sAttributeKey];
2537+
if (_attribute && ![_attribute isKindOfClass:[NSNumber class]]) {
2538+
MTR_LOG_ERROR("MTRAttributePath decoded %@ for attribute, not NSNumber.", _attribute);
2539+
return nil;
2540+
}
2541+
2542+
return self;
2543+
}
2544+
2545+
- (void)encodeWithCoder:(NSCoder *)coder
2546+
{
2547+
[coder encodeObject:_attribute forKey:sAttributeKey];
2548+
}
2549+
24852550
@end
24862551

24872552
@implementation MTRAttributePath (Deprecated)

src/darwin/Framework/CHIP/MTRDevice.mm

+43-13
Original file line numberDiff line numberDiff line change
@@ -1503,15 +1503,20 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
15031503

15041504
NSMutableArray * attributesToReport = [NSMutableArray array];
15051505
NSMutableArray * attributePathsToReport = [NSMutableArray array];
1506-
for (NSDictionary<NSString *, id> * attributeReponseValue in reportedAttributeValues) {
1507-
MTRAttributePath * attributePath = attributeReponseValue[MTRAttributePathKey];
1508-
NSDictionary * attributeDataValue = attributeReponseValue[MTRDataKey];
1509-
NSError * attributeError = attributeReponseValue[MTRErrorKey];
1506+
BOOL dataStoreExists = _deviceController.controllerDataStore != nil;
1507+
NSMutableArray * attributesToPersist;
1508+
if (dataStoreExists) {
1509+
attributesToPersist = [NSMutableArray array];
1510+
}
1511+
for (NSDictionary<NSString *, id> * attributeResponseValue in reportedAttributeValues) {
1512+
MTRAttributePath * attributePath = attributeResponseValue[MTRAttributePathKey];
1513+
NSDictionary * attributeDataValue = attributeResponseValue[MTRDataKey];
1514+
NSError * attributeError = attributeResponseValue[MTRErrorKey];
15101515
NSDictionary * previousValue;
15111516

15121517
// sanity check either data value or error must exist
15131518
if (!attributeDataValue && !attributeError) {
1514-
MTR_LOG_INFO("%@ report %@ no data value or error: %@", self, attributePath, attributeReponseValue);
1519+
MTR_LOG_INFO("%@ report %@ no data value or error: %@", self, attributePath, attributeResponseValue);
15151520
continue;
15161521
}
15171522

@@ -1532,6 +1537,12 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
15321537
previousValue = _readCache[attributePath];
15331538
_readCache[attributePath] = nil;
15341539
} else {
1540+
BOOL readCacheValueChanged = ![self _attributeDataValue:attributeDataValue isEqualToDataValue:_readCache[attributePath]];
1541+
// Check if attribute needs to be persisted - compare only to read cache and disregard expected values
1542+
if (dataStoreExists && readCacheValueChanged) {
1543+
[attributesToPersist addObject:attributeResponseValue];
1544+
}
1545+
15351546
// if expected values exists, purge and update read cache
15361547
NSArray * expectedValue = _expectedValueCache[attributePath];
15371548
if (expectedValue) {
@@ -1542,7 +1553,7 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
15421553
}
15431554
_expectedValueCache[attributePath] = nil;
15441555
_readCache[attributePath] = attributeDataValue;
1545-
} else if (![self _attributeDataValue:attributeDataValue isEqualToDataValue:_readCache[attributePath]]) {
1556+
} else if (readCacheValueChanged) {
15461557
// otherwise compare and update read cache
15471558
previousValue = _readCache[attributePath];
15481559
_readCache[attributePath] = attributeDataValue;
@@ -1580,21 +1591,40 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
15801591

15811592
if (shouldReportAttribute) {
15821593
if (previousValue) {
1583-
NSMutableDictionary * mutableAttributeReponseValue = attributeReponseValue.mutableCopy;
1584-
mutableAttributeReponseValue[MTRPreviousDataKey] = previousValue;
1585-
[attributesToReport addObject:mutableAttributeReponseValue];
1594+
NSMutableDictionary * mutableAttributeResponseValue = attributeResponseValue.mutableCopy;
1595+
mutableAttributeResponseValue[MTRPreviousDataKey] = previousValue;
1596+
[attributesToReport addObject:mutableAttributeResponseValue];
15861597
} else {
1587-
[attributesToReport addObject:attributeReponseValue];
1598+
[attributesToReport addObject:attributeResponseValue];
15881599
}
15891600
[attributePathsToReport addObject:attributePath];
15901601
}
15911602
}
15921603

15931604
MTR_LOG_INFO("%@ report from reported values %@", self, attributePathsToReport);
15941605

1606+
if (dataStoreExists && attributesToPersist.count) {
1607+
[_deviceController.controllerDataStore storeAttributeValues:attributesToPersist forNodeID:_nodeID];
1608+
}
1609+
15951610
return attributesToReport;
15961611
}
15971612

1613+
- (void)setAttributeValues:(NSArray<NSDictionary *> *)attributeValues reportChanges:(BOOL)reportChanges
1614+
{
1615+
if (reportChanges) {
1616+
[self _handleAttributeReport:attributeValues];
1617+
} else {
1618+
os_unfair_lock_lock(&self->_lock);
1619+
for (NSDictionary * responseValue in attributeValues) {
1620+
MTRAttributePath * path = responseValue[MTRAttributePathKey];
1621+
NSDictionary * dataValue = responseValue[MTRDataKey];
1622+
_readCache[path] = dataValue;
1623+
}
1624+
os_unfair_lock_unlock(&self->_lock);
1625+
}
1626+
}
1627+
15981628
// If value is non-nil, associate with expectedValueID
15991629
// If value is nil, remove only if expectedValueID matches
16001630
// previousValue is an out parameter
@@ -1662,9 +1692,9 @@ - (NSArray *)_getAttributesToReportWithNewExpectedValues:(NSArray<NSDictionary<N
16621692

16631693
NSMutableArray * attributesToReport = [NSMutableArray array];
16641694
NSMutableArray * attributePathsToReport = [NSMutableArray array];
1665-
for (NSDictionary<NSString *, id> * attributeReponseValue in expectedAttributeValues) {
1666-
MTRAttributePath * attributePath = attributeReponseValue[MTRAttributePathKey];
1667-
NSDictionary * attributeDataValue = attributeReponseValue[MTRDataKey];
1695+
for (NSDictionary<NSString *, id> * attributeResponseValue in expectedAttributeValues) {
1696+
MTRAttributePath * attributePath = attributeResponseValue[MTRAttributePathKey];
1697+
NSDictionary * attributeDataValue = attributeResponseValue[MTRDataKey];
16681698

16691699
BOOL shouldReportValue = NO;
16701700
NSDictionary<NSString *, id> * attributeValueToReport;

src/darwin/Framework/CHIP/MTRDeviceController.mm

+6
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,12 @@ - (MTRDevice *)deviceForNodeID:(NSNumber *)nodeID
864864
if ([self isRunning]) {
865865
_nodeIDToDeviceMap[nodeID] = deviceToReturn;
866866
}
867+
868+
// Load persisted attributes if they exist.
869+
NSArray * attributesFromCache = [_controllerDataStore getStoredAttributesForNodeID:nodeID];
870+
if (attributesFromCache) {
871+
[deviceToReturn setAttributeValues:attributesFromCache reportChanges:NO];
872+
}
867873
}
868874
os_unfair_lock_unlock(&_deviceMapLock);
869875

src/darwin/Framework/CHIP/MTRDeviceControllerDataStore.h

+8
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ NS_ASSUME_NONNULL_BEGIN
6363
- (CHIP_ERROR)storeLastLocallyUsedNOC:(MTRCertificateTLVBytes)noc;
6464
- (MTRCertificateTLVBytes _Nullable)fetchLastLocallyUsedNOC;
6565

66+
/**
67+
* 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.
68+
*/
69+
- (nullable NSArray<NSDictionary *> *)getStoredAttributesForNodeID:(NSNumber *)nodeID;
70+
- (void)storeAttributeValues:(NSArray<NSDictionary *> *)dataValues forNodeID:(NSNumber *)nodeID;
71+
- (void)clearStoredAttributesForNodeID:(NSNumber *)nodeID;
72+
- (void)clearAllStoredAttributes;
73+
6674
@end
6775

6876
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)