@@ -376,7 +376,11 @@ @implementation MTRDevice {
376
376
#ifdef DEBUG
377
377
NSUInteger _unitTestAttributesReportedSinceLastCheck;
378
378
#endif
379
- BOOL _delegateDeviceCachePrimedCalled;
379
+
380
+ // _deviceCachePrimed is true if we have the data that comes from an initial
381
+ // subscription priming report (whether it came from storage or from our
382
+ // subscription).
383
+ BOOL _deviceCachePrimed;
380
384
381
385
// _persistedClusterData stores data that we have already persisted (when we have
382
386
// cluster data persistence enabled). Nil when we have no persistence enabled.
@@ -736,11 +740,6 @@ - (void)setDelegate:(id<MTRDeviceDelegate>)delegate queue:(dispatch_queue_t)queu
736
740
_weakDelegate = [MTRWeakReference weakReferenceWithObject: delegate];
737
741
_delegateQueue = queue;
738
742
739
- // If Check if cache is already primed and client hasn't been informed yet, call the -deviceCachePrimed: callback
740
- if (!_delegateDeviceCachePrimedCalled && [self _isCachePrimedWithInitialConfigurationData ]) {
741
- [self _callDelegateDeviceCachePrimed ];
742
- }
743
-
744
743
if (setUpSubscription) {
745
744
_initialSubscribeStart = [NSDate now ];
746
745
if ([self _deviceUsesThread ]) {
@@ -915,7 +914,7 @@ - (BOOL)_callDelegateWithBlock:(void (^)(id<MTRDeviceDelegate>))block
915
914
- (void )_callDelegateDeviceCachePrimed
916
915
{
917
916
os_unfair_lock_assert_owner (&self->_lock );
918
- _delegateDeviceCachePrimedCalled = [self _callDelegateWithBlock: ^(id <MTRDeviceDelegate> delegate) {
917
+ [self _callDelegateWithBlock: ^(id <MTRDeviceDelegate> delegate) {
919
918
if ([delegate respondsToSelector: @selector (deviceCachePrimed: )]) {
920
919
[delegate deviceCachePrimed: self ];
921
920
}
@@ -994,11 +993,6 @@ - (void)_handleSubscriptionEstablished
994
993
[self _changeInternalState: MTRInternalDeviceStateInitialSubscriptionEstablished];
995
994
}
996
995
997
- // As subscription is established, check if the delegate needs to be informed
998
- if (!_delegateDeviceCachePrimedCalled) {
999
- [self _callDelegateDeviceCachePrimed ];
1000
- }
1001
-
1002
996
[self _changeState: MTRDeviceStateReachable];
1003
997
1004
998
// No need to monitor connectivity after subscription establishment
@@ -1480,6 +1474,13 @@ - (void)_scheduleClusterDataPersistence
1480
1474
return ;
1481
1475
}
1482
1476
1477
+ // If we have nothing stored at all yet, store directly, so we move into a
1478
+ // primed state.
1479
+ if (!_deviceCachePrimed) {
1480
+ [self _persistClusterData ];
1481
+ return ;
1482
+ }
1483
+
1483
1484
// Ensure there is an array to keep the most recent report times
1484
1485
if (!_mostRecentReportTimes) {
1485
1486
_mostRecentReportTimes = [NSMutableArray array ];
@@ -1525,7 +1526,7 @@ - (void)_scheduleClusterDataPersistence
1525
1526
1526
1527
// Set current multiplier to [1, MaxMultiplier]
1527
1528
_reportToPersistenceDelayCurrentMultiplier = 1 + (proportionTowardMinThreshold * (_storageBehaviorConfiguration.reportToPersistenceDelayMaxMultiplier - 1 ));
1528
- MTR_LOG (" %@ storage behavior: device reporting frequently - setting delay multiplied to %lf" , self, _reportToPersistenceDelayCurrentMultiplier);
1529
+ MTR_LOG (" %@ storage behavior: device reporting frequently - setting delay multiplier to %lf" , self, _reportToPersistenceDelayCurrentMultiplier);
1529
1530
} else {
1530
1531
_reportToPersistenceDelayCurrentMultiplier = 1 ;
1531
1532
}
@@ -1601,6 +1602,19 @@ - (void)_handleReportEnd
1601
1602
_deviceConfigurationChanged = NO ;
1602
1603
}
1603
1604
1605
+ // Do this after the _deviceConfigurationChanged check, so that we don't
1606
+ // call deviceConfigurationChanged: immediately after telling our delegate
1607
+ // we are now primed.
1608
+ //
1609
+ // TODO: Maybe we shouldn't dispatch deviceConfigurationChanged: for the
1610
+ // initial priming bits?
1611
+ if (!_deviceCachePrimed) {
1612
+ // This is the end of the priming sequence of data reports, so we have
1613
+ // all the data for the device now.
1614
+ _deviceCachePrimed = YES ;
1615
+ [self _callDelegateDeviceCachePrimed ];
1616
+ }
1617
+
1604
1618
// For unit testing only
1605
1619
#ifdef DEBUG
1606
1620
id delegate = _weakDelegate.strongObject ;
@@ -3165,10 +3179,9 @@ - (void)setPersistedClusterData:(NSDictionary<MTRClusterPath *, MTRDeviceCluster
3165
3179
[_persistedClusterData setObject: clusterData[clusterPath] forKey: clusterPath];
3166
3180
}
3167
3181
3168
- // If cache is set from storage and is primed with initial configuration data, then assume the client had beeen informed in the past, and mark that the callback has been called
3169
- if ([self _isCachePrimedWithInitialConfigurationData ]) {
3170
- _delegateDeviceCachePrimedCalled = YES ;
3171
- }
3182
+ // We have some stored data. Since we don't store data until the end of the
3183
+ // initial priming report, our device cache must be primed.
3184
+ _deviceCachePrimed = YES ;
3172
3185
}
3173
3186
3174
3187
- (void )_setLastInitialSubscribeLatency : (id )latency
@@ -3218,7 +3231,7 @@ - (void)_storePersistedDeviceData
3218
3231
- (BOOL )deviceCachePrimed
3219
3232
{
3220
3233
std::lock_guard lock (_lock);
3221
- return [ self _isCachePrimedWithInitialConfigurationData ] ;
3234
+ return _deviceCachePrimed ;
3222
3235
}
3223
3236
3224
3237
// If value is non-nil, associate with expectedValueID
@@ -3395,47 +3408,6 @@ - (void)_removeExpectedValueForAttributePath:(MTRAttributePath *)attributePath e
3395
3408
}
3396
3409
}
3397
3410
3398
- // This method checks if there is a need to inform delegate that the attribute cache has been "primed"
3399
- - (BOOL )_isCachePrimedWithInitialConfigurationData
3400
- {
3401
- os_unfair_lock_assert_owner (&self->_lock );
3402
-
3403
- // Check if root node descriptor exists
3404
- MTRDeviceDataValueDictionary rootDescriptorPartsListDataValue = [self _cachedAttributeValueForPath: [MTRAttributePath attributePathWithEndpointID: @(kRootEndpointId ) clusterID: @(MTRClusterIDTypeDescriptorID) attributeID: @(MTRAttributeIDTypeClusterDescriptorAttributePartsListID)]];
3405
- if (!rootDescriptorPartsListDataValue || ![MTRArrayValueType isEqualToString: rootDescriptorPartsListDataValue[MTRTypeKey]]) {
3406
- return NO ;
3407
- }
3408
- NSArray * partsList = rootDescriptorPartsListDataValue[MTRValueKey];
3409
- if (![partsList isKindOfClass: [NSArray class ]] || !partsList.count ) {
3410
- MTR_LOG_ERROR (" %@ unexpected type %@ for parts list %@" , self, [partsList class ], partsList);
3411
- return NO ;
3412
- }
3413
-
3414
- // Check if we have cached descriptor clusters for each listed endpoint
3415
- for (NSDictionary * endpointDictionary in partsList) {
3416
- NSDictionary * endpointDataValue = endpointDictionary[MTRDataKey];
3417
- if (![endpointDataValue isKindOfClass: [NSDictionary class ]]) {
3418
- MTR_LOG_ERROR (" %@ unexpected parts list dictionary %@ data value class %@" , self, endpointDictionary, [endpointDataValue class ]);
3419
- continue ;
3420
- }
3421
- if (![MTRUnsignedIntegerValueType isEqual: endpointDataValue[MTRTypeKey]]) {
3422
- MTR_LOG_ERROR (" %@ unexpected parts list data value %@ item type %@" , self, endpointDataValue, endpointDataValue[MTRTypeKey]);
3423
- continue ;
3424
- }
3425
- NSNumber * endpoint = endpointDataValue[MTRValueKey];
3426
- if (![endpoint isKindOfClass: [NSNumber class ]]) {
3427
- MTR_LOG_ERROR (" %@ unexpected parts list item value class %@" , self, [endpoint class ]);
3428
- continue ;
3429
- }
3430
- MTRDeviceDataValueDictionary descriptorDeviceTypeListDataValue = [self _cachedAttributeValueForPath: [MTRAttributePath attributePathWithEndpointID: endpoint clusterID: @(MTRClusterIDTypeDescriptorID) attributeID: @(MTRAttributeIDTypeClusterDescriptorAttributeDeviceTypeListID)]];
3431
- if (![MTRArrayValueType isEqualToString: descriptorDeviceTypeListDataValue[MTRTypeKey]] || !descriptorDeviceTypeListDataValue[MTRValueKey]) {
3432
- return NO ;
3433
- }
3434
- }
3435
-
3436
- return YES ;
3437
- }
3438
-
3439
3411
- (MTRBaseDevice *)newBaseDevice
3440
3412
{
3441
3413
return [MTRBaseDevice deviceWithNodeID: self .nodeID controller: self .deviceController];
0 commit comments