@@ -649,21 +649,7 @@ - (void)_setDSTOffsets:(NSArray<MTRTimeSynchronizationClusterDSTOffsetStruct *>
649
649
return outputArray;
650
650
}
651
651
652
- #pragma mark Subscription and delegate handling
653
-
654
- // subscription intervals are in seconds
655
- #define MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MIN (10 * 60 ) // 10 minutes (for now)
656
- #define MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MAX (60 * 60 ) // 60 minutes
657
-
658
- - (BOOL )_subscriptionsAllowed
659
- {
660
- os_unfair_lock_assert_owner (&self->_lock );
661
-
662
- // TODO: XPC: This function and all its callsites should go away from this class.
663
-
664
- // We should not allow a subscription for device controllers over XPC.
665
- return ![_deviceController isKindOfClass: MTRDeviceControllerOverXPC.class ];
666
- }
652
+ #pragma mark Delegate handling
667
653
668
654
- (void )setDelegate : (id <MTRDeviceDelegate>)delegate queue : (dispatch_queue_t )queue
669
655
{
@@ -757,41 +743,6 @@ - (void)nodeMayBeAdvertisingOperational
757
743
MTR_LOG (" %@ saw new operational advertisement" , self);
758
744
}
759
745
760
- // Return YES if we are in a state where, apart from communication issues with
761
- // the device, we will be able to get reports via our subscription.
762
- - (BOOL )_subscriptionAbleToReport
763
- {
764
- std::lock_guard lock (_lock);
765
- if (![self _delegateExists ]) {
766
- // No delegate definitely means no subscription.
767
- return NO ;
768
- }
769
-
770
- // For unit testing only, matching logic in setDelegate
771
- #ifdef DEBUG
772
- __block BOOL useTestDelegateOverride = NO ;
773
- __block BOOL testDelegateShouldSetUpSubscriptionForDevice = NO ;
774
- [self _callFirstDelegateSynchronouslyWithBlock: ^(id testDelegate) {
775
- if ([testDelegate respondsToSelector: @selector (unitTestShouldSetUpSubscriptionForDevice: )]) {
776
- useTestDelegateOverride = YES ;
777
- testDelegateShouldSetUpSubscriptionForDevice = [testDelegate unitTestShouldSetUpSubscriptionForDevice: self ];
778
- }
779
- }];
780
- if (useTestDelegateOverride && !testDelegateShouldSetUpSubscriptionForDevice) {
781
- return NO ;
782
- }
783
-
784
- #endif
785
-
786
- // Subscriptions are not able to report if they are not allowed.
787
- return [self _subscriptionsAllowed ];
788
- }
789
-
790
- // Notification that read-through was skipped for an attribute read.
791
- - (void )_readThroughSkipped
792
- {
793
- }
794
-
795
746
- (BOOL )_delegateExists
796
747
{
797
748
os_unfair_lock_assert_owner (&self->_lock );
@@ -1880,147 +1831,12 @@ static BOOL AttributeHasChangesOmittedQuality(MTRAttributePath * attributePath)
1880
1831
attributeID : (NSNumber *)attributeID
1881
1832
params : (MTRReadParams * _Nullable)params
1882
1833
{
1883
- MTRAttributePath * attributePath = [MTRAttributePath attributePathWithEndpointID: endpointID
1884
- clusterID: clusterID
1885
- attributeID: attributeID];
1886
-
1887
- BOOL attributeIsSpecified = MTRAttributeIsSpecified (clusterID.unsignedIntValue , attributeID.unsignedIntValue );
1888
- BOOL hasChangesOmittedQuality;
1889
- if (attributeIsSpecified) {
1890
- hasChangesOmittedQuality = AttributeHasChangesOmittedQuality (attributePath);
1891
- } else {
1892
- if (params == nil ) {
1893
- hasChangesOmittedQuality = NO ;
1894
- } else {
1895
- hasChangesOmittedQuality = !params.assumeUnknownAttributesReportable ;
1896
- }
1897
- }
1898
-
1899
- // Return current known / expected value right away
1900
- NSDictionary <NSString *, id > * attributeValueToReturn = [self _attributeValueDictionaryForAttributePath: attributePath];
1901
-
1902
- // Send read request to device if any of the following are true:
1903
- // 1. Subscription not in a state we can expect reports
1904
- // 2. The attribute has the Changes Omitted quality, so we won't get reports for it.
1905
- // 3. The attribute is not in the spec, and the read params asks to assume
1906
- // an unknown attribute has the Changes Omitted quality.
1907
- if (![self _subscriptionAbleToReport ] || hasChangesOmittedQuality) {
1908
- // Read requests container will be a mutable array of items, each being an array containing:
1909
- // [attribute request path, params]
1910
- // Batching handler should only coalesce when params are equal.
1911
-
1912
- // For this single read API there's only 1 array item. Use NSNull to stand in for nil params for easy comparison.
1913
- MTRAttributeRequestPath * readRequestPath = [MTRAttributeRequestPath requestPathWithEndpointID: endpointID
1914
- clusterID: clusterID
1915
- attributeID: attributeID];
1916
- NSArray * readRequestData = @[ readRequestPath, params ?: [NSNull null ] ];
1917
-
1918
- // But first, check if a duplicate read request is already queued and return
1919
- if ([_asyncWorkQueue hasDuplicateForTypeID: MTRDeviceWorkItemDuplicateReadTypeID workItemData: readRequestData]) {
1920
- return attributeValueToReturn;
1921
- }
1922
-
1923
- NSMutableArray <NSArray *> * readRequests = [NSMutableArray arrayWithObject: readRequestData];
1924
-
1925
- // Create work item, set ready handler to perform task, then enqueue the work
1926
- MTRAsyncWorkItem * workItem = [[MTRAsyncWorkItem alloc ] initWithQueue: self .queue];
1927
- uint64_t workItemID = workItem.uniqueID ; // capture only the ID, not the work item
1928
- NSNumber * nodeID = [self nodeID ];
1929
-
1930
- [workItem setBatchingID: MTRDeviceWorkItemBatchingReadID data: readRequests handler: ^(id opaqueDataCurrent, id opaqueDataNext) {
1931
- mtr_hide (self); // don't capture self accidentally
1932
- NSMutableArray <NSArray *> * readRequestsCurrent = opaqueDataCurrent;
1933
- NSMutableArray <NSArray *> * readRequestsNext = opaqueDataNext;
1934
-
1935
- MTRBatchingOutcome outcome = MTRNotBatched;
1936
- while (readRequestsNext.count ) {
1937
- // Can only read up to 9 paths at a time, per spec
1938
- if (readRequestsCurrent.count >= 9 ) {
1939
- MTR_LOG (" Batching read attribute work item [%llu]: cannot add more work, item is full [0x%016llX:%@:0x%llx:0x%llx]" , workItemID, nodeID.unsignedLongLongValue , endpointID, clusterID.unsignedLongLongValue , attributeID.unsignedLongLongValue );
1940
- return outcome;
1941
- }
1942
-
1943
- // if params don't match then they cannot be merged
1944
- if (![readRequestsNext[0 ][MTRDeviceReadRequestFieldParamsIndex]
1945
- isEqual: readRequestsCurrent[0 ][MTRDeviceReadRequestFieldParamsIndex]]) {
1946
- MTR_LOG (" Batching read attribute work item [%llu]: cannot add more work, parameter mismatch [0x%016llX:%@:0x%llx:0x%llx]" , workItemID, nodeID.unsignedLongLongValue , endpointID, clusterID.unsignedLongLongValue , attributeID.unsignedLongLongValue );
1947
- return outcome;
1948
- }
1949
-
1950
- // merge the next item's first request into the current item's list
1951
- auto readItem = readRequestsNext.firstObject ;
1952
- [readRequestsNext removeObjectAtIndex: 0 ];
1953
- [readRequestsCurrent addObject: readItem];
1954
- MTR_LOG (" Batching read attribute work item [%llu]: added %@ (now %lu requests total) [0x%016llX:%@:0x%llx:0x%llx]" ,
1955
- workItemID, readItem, static_cast <unsigned long >(readRequestsCurrent.count ), nodeID.unsignedLongLongValue , endpointID, clusterID.unsignedLongLongValue , attributeID.unsignedLongLongValue );
1956
- outcome = MTRBatchedPartially;
1957
- }
1958
- NSCAssert (readRequestsNext.count == 0 , @" should have batched everything or returned early" );
1959
- return MTRBatchedFully;
1960
- }];
1961
- [workItem setDuplicateTypeID: MTRDeviceWorkItemDuplicateReadTypeID handler: ^(id opaqueItemData, BOOL * isDuplicate, BOOL * stop) {
1962
- mtr_hide (self); // don't capture self accidentally
1963
- for (NSArray * readItem in readRequests) {
1964
- if ([readItem isEqual: opaqueItemData]) {
1965
- MTR_LOG (" Read attribute work item [%llu] report duplicate %@ [0x%016llX:%@:0x%llx:0x%llx]" , workItemID, readItem, nodeID.unsignedLongLongValue , endpointID, clusterID.unsignedLongLongValue , attributeID.unsignedLongLongValue );
1966
- *isDuplicate = YES ;
1967
- *stop = YES ;
1968
- return ;
1969
- }
1970
- }
1971
- *stop = NO ;
1972
- }];
1973
- [workItem setReadyHandler: ^(MTRDevice * self , NSInteger retryCount, MTRAsyncWorkCompletionBlock completion) {
1974
- // Sanity check
1975
- if (readRequests.count == 0 ) {
1976
- MTR_LOG_ERROR (" Read attribute work item [%llu] contained no read requests" , workItemID);
1977
- completion (MTRAsyncWorkComplete);
1978
- return ;
1979
- }
1980
-
1981
- // Build the attribute paths from the read requests
1982
- NSMutableArray <MTRAttributeRequestPath *> * attributePaths = [NSMutableArray array ];
1983
- for (NSArray * readItem in readRequests) {
1984
- NSAssert (readItem.count == 2 , @" invalid read attribute item" );
1985
- [attributePaths addObject: readItem[MTRDeviceReadRequestFieldPathIndex]];
1986
- }
1987
- // If param is the NSNull stand-in, then just use nil
1988
- id readParamObject = readRequests[0 ][MTRDeviceReadRequestFieldParamsIndex];
1989
- MTRReadParams * readParams = (![readParamObject isEqual: [NSNull null ]]) ? readParamObject : nil ;
1990
-
1991
- MTRBaseDevice * baseDevice = [self newBaseDevice ];
1992
- [baseDevice
1993
- readAttributePaths: attributePaths
1994
- eventPaths: nil
1995
- params: readParams
1996
- includeDataVersion: YES
1997
- queue: self .queue
1998
- completion: ^(NSArray <NSDictionary <NSString *, id > *> * _Nullable values, NSError * _Nullable error) {
1999
- if (values) {
2000
- // Since the format is the same data-value dictionary, this looks like an
2001
- // attribute report
2002
- MTR_LOG (" Read attribute work item [%llu] result: %@ [0x%016llX:%@:0x%llX:0x%llX]" , workItemID, values, nodeID.unsignedLongLongValue , endpointID, clusterID.unsignedLongLongValue , attributeID.unsignedLongLongValue );
2003
- [self _handleAttributeReport: values fromSubscription: NO ];
2004
- }
2005
-
2006
- // TODO: better retry logic
2007
- if (error && (retryCount < 2 )) {
2008
- MTR_LOG_ERROR (" Read attribute work item [%llu] failed (will retry): %@ [0x%016llX:%@:0x%llx:0x%llx]" , workItemID, error, nodeID.unsignedLongLongValue , endpointID, clusterID.unsignedLongLongValue , attributeID.unsignedLongLongValue );
2009
- completion (MTRAsyncWorkNeedsRetry);
2010
- } else {
2011
- if (error) {
2012
- MTR_LOG (" Read attribute work item [%llu] failed (giving up): %@ [0x%016llX:%@:0x%llx:0x%llx]" , workItemID, error, nodeID.unsignedLongLongValue , endpointID, clusterID.unsignedLongLongValue , attributeID.unsignedLongLongValue );
2013
- }
2014
- completion (MTRAsyncWorkComplete);
2015
- }
2016
- }];
2017
- }];
2018
- [_asyncWorkQueue enqueueWorkItem: workItem descriptionWithFormat: @" read %@ 0x%llx 0x%llx " , endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue];
2019
- } else {
2020
- [self _readThroughSkipped ];
2021
- }
2022
-
2023
- return attributeValueToReturn;
1834
+ #define ErrorStr " MTRDevice readAttributeWithEndpointID:clusterID:attributeID:params: must be handled by subclasses"
1835
+ MTR_LOG_ERROR (ErrorStr);
1836
+ #ifdef DEBUG
1837
+ NSAssert (NO , @ErrorStr);
1838
+ #endif // DEBUG
1839
+ return nil ;
2024
1840
}
2025
1841
2026
1842
- (void )writeAttributeWithEndpointID : (NSNumber *)endpointID
@@ -2456,37 +2272,6 @@ - (void)_performScheduledExpirationCheck
2456
2272
[self _checkExpiredExpectedValues ];
2457
2273
}
2458
2274
2459
- // Get attribute value dictionary for an attribute path from the right cache
2460
- - (NSDictionary <NSString *, id> *)_attributeValueDictionaryForAttributePath : (MTRAttributePath *)attributePath
2461
- {
2462
- std::lock_guard lock (_lock);
2463
-
2464
- // First check expected value cache
2465
- NSArray * expectedValue = _expectedValueCache[attributePath];
2466
- if (expectedValue) {
2467
- NSDate * now = [NSDate date ];
2468
- if ([now compare: expectedValue[MTRDeviceExpectedValueFieldExpirationTimeIndex]] == NSOrderedDescending) {
2469
- // expired - purge and fall through
2470
- _expectedValueCache[attributePath] = nil ;
2471
- } else {
2472
- // not yet expired - return result
2473
- return expectedValue[MTRDeviceExpectedValueFieldValueIndex];
2474
- }
2475
- }
2476
-
2477
- // Then check read cache
2478
- NSDictionary <NSString *, id > * cachedAttributeValue = [self _cachedAttributeValueForPath: attributePath];
2479
- if (cachedAttributeValue) {
2480
- return cachedAttributeValue;
2481
- } else {
2482
- // TODO: when not found in cache, generated default values should be used
2483
- MTR_LOG (" %@ _attributeValueDictionaryForAttributePath: could not find cached attribute values for attribute %@" , self,
2484
- attributePath);
2485
- }
2486
-
2487
- return nil ;
2488
- }
2489
-
2490
2275
- (BOOL )_attributeDataValue : (NSDictionary *)one isEqualToDataValue : (NSDictionary *)theOther
2491
2276
{
2492
2277
// Sanity check for nil cases
0 commit comments