From 8a42ca96c90c51ea97b76d8c25bcd6e110402f2e Mon Sep 17 00:00:00 2001
From: Boris Zbarsky <bzbarsky@apple.com>
Date: Wed, 18 Sep 2024 14:38:31 -0400
Subject: [PATCH 1/2] Fix behavior of MTRDevice readAttributePaths if it
 happens mid-priming.

We might not know the values of AttributeList/ServerList/PartsList yet at that
point.  Just go ahead and figure out which attribute values we have by walking
our own data structures, not the metadata list attribute values.
---
 .../Framework/CHIP/MTRDevice_Concrete.mm      | 72 +++++++------------
 1 file changed, 25 insertions(+), 47 deletions(-)

diff --git a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm
index d741d0124945fb..f7c888060aaf84 100644
--- a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm
+++ b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm
@@ -2983,11 +2983,31 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID
     // Determine the set of what the spec calls "existent paths" that correspond
     // to the request paths.  Building the whole set in-memory is OK, because
     // we're going to need all those paths for our return value anyway.
+    //
+    // Note that we don't use the structural attributes (PartsList, ServerList,
+    // AttributeList) to determine this set, because we might be in the middle
+    // of priming right now and not have gotten those yet.  Just use the set of
+    // attribute paths we actually have.
     NSMutableSet<MTRAttributePath *> * existentPaths = [[NSMutableSet alloc] init];
     {
         std::lock_guard lock(_lock);
-        for (MTRAttributeRequestPath * path in attributePaths) {
-            [self _addExistentPathsFor:path to:existentPaths];
+        for (MTRAttributeRequestPath * requestPath in attributePaths) {
+            for (MTRClusterPath * clusterPath in [self _knownClusters]) {
+                if (requestPath.endpoint != nil && ![requestPath.endpoint isEqual:clusterPath.endpoint]) {
+                    continue;
+                }
+                if (requestPath.cluster != nil && ![requestPath.cluster isEqual:clusterPath.cluster]) {
+                    continue;
+                }
+                MTRDeviceClusterData * clusterData = [self _clusterDataForPath:clusterPath];
+                if (requestPath.attribute == nil) {
+                    for (NSNumber * attributeID in clusterData.attributes) {
+                        [existentPaths addObject:[MTRAttributePath attributePathWithEndpointID:clusterPath.endpoint clusterID:clusterPath.cluster attributeID:attributeID]];
+                    }
+                } else if ([clusterData.attributes objectForKey:requestPath.attribute] != nil) {
+                    [existentPaths addObject:[MTRAttributePath attributePathWithEndpointID:clusterPath.endpoint clusterID:clusterPath.cluster attributeID:requestPath.attribute]];
+                }
+            }
         }
     }
 
@@ -3006,51 +3026,6 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID
     return result;
 }
 
-- (void)_addExistentPathsFor:(MTRAttributeRequestPath *)path to:(NSMutableSet<MTRAttributePath *> *)set
-{
-    os_unfair_lock_assert_owner(&_lock);
-
-    if (path.endpoint != nil) {
-        [self _addExistentPathsForEndpoint:path.endpoint path:path to:set];
-        return;
-    }
-
-    NSArray<NSNumber *> * endpointList = [self _endpointList];
-    for (NSNumber * endpoint in endpointList) {
-        [self _addExistentPathsForEndpoint:endpoint path:path to:set];
-    }
-}
-
-- (void)_addExistentPathsForEndpoint:(NSNumber *)endpoint path:(MTRAttributeRequestPath *)path to:(NSMutableSet<MTRAttributePath *> *)set
-{
-    os_unfair_lock_assert_owner(&_lock);
-
-    if (path.cluster != nil) {
-        [self _addExistentPathsForEndpoint:endpoint cluster:path.cluster attribute:path.attribute to:set];
-        return;
-    }
-
-    auto * clusterList = [self _serverListForEndpointID:endpoint];
-    for (NSNumber * cluster in clusterList) {
-        [self _addExistentPathsForEndpoint:endpoint cluster:cluster attribute:path.attribute to:set];
-    }
-}
-
-- (void)_addExistentPathsForEndpoint:(NSNumber *)endpoint cluster:(NSNumber *)cluster attribute:(NSNumber * _Nullable)attribute to:(NSMutableSet<MTRAttributePath *> *)set
-{
-    os_unfair_lock_assert_owner(&_lock);
-
-    if (attribute != nil) {
-        [set addObject:[MTRAttributePath attributePathWithEndpointID:endpoint clusterID:cluster attributeID:attribute]];
-        return;
-    }
-
-    auto * attributeList = [self _attributeListForEndpointID:endpoint clusterID:cluster];
-    for (NSNumber * existentAttribute in attributeList) {
-        [set addObject:[MTRAttributePath attributePathWithEndpointID:endpoint clusterID:cluster attributeID:existentAttribute]];
-    }
-}
-
 - (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID
                            clusterID:(NSNumber *)clusterID
                            commandID:(NSNumber *)commandID
@@ -3652,6 +3627,9 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
     return attributesToReport;
 }
 
+// TODO: Figure out whether we can get rid of this in favor of readAttributePaths.  This differs from
+// readAttributePaths in one respect: that function will do read-through for
+// C-quality attributes, but this one does not.
 - (NSArray<NSDictionary<NSString *, id> *> *)getAllAttributesReport
 {
     std::lock_guard lock(_lock);

From 129bd9757970bcd6412c47feca0ba5b3d4b60039 Mon Sep 17 00:00:00 2001
From: Boris Zbarsky <bzbarsky@apple.com>
Date: Fri, 20 Sep 2024 14:23:06 -0400
Subject: [PATCH 2/2] Apply suggestion from code review.

Co-authored-by: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com>
---
 src/darwin/Framework/CHIP/MTRDevice_Concrete.mm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm
index f7c888060aaf84..bc26905d174834 100644
--- a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm
+++ b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm
@@ -2986,7 +2986,7 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID
     //
     // Note that we don't use the structural attributes (PartsList, ServerList,
     // AttributeList) to determine this set, because we might be in the middle
-    // of priming right now and not have gotten those yet.  Just use the set of
+    // of priming right now and have not gotten those yet.  Just use the set of
     // attribute paths we actually have.
     NSMutableSet<MTRAttributePath *> * existentPaths = [[NSMutableSet alloc] init];
     {