From 5d42d920de46da694d69ddc6500faf5834638e72 Mon Sep 17 00:00:00 2001
From: Boris Zbarsky <bzbarsky@apple.com>
Date: Mon, 9 Dec 2024 20:03:50 -0500
Subject: [PATCH] Add a helper method to get the descriptor cluster info from
 MTRDevice. (#36769)

---
 src/darwin/Framework/CHIP/MTRDevice.h         |  9 +++++++
 src/darwin/Framework/CHIP/MTRDevice.mm        | 26 +++++++++++++++++++
 .../Framework/CHIPTests/MTRDeviceTests.m      | 16 ++++++++++++
 3 files changed, 51 insertions(+)

diff --git a/src/darwin/Framework/CHIP/MTRDevice.h b/src/darwin/Framework/CHIP/MTRDevice.h
index b1ca179df658f7..ae355605626ad9 100644
--- a/src/darwin/Framework/CHIP/MTRDevice.h
+++ b/src/darwin/Framework/CHIP/MTRDevice.h
@@ -221,6 +221,15 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1))
  */
 - (NSArray<NSDictionary<NSString *, id> *> *)readAttributePaths:(NSArray<MTRAttributeRequestPath *> *)attributePaths MTR_AVAILABLE(ios(18.2), macos(15.2), watchos(11.2), tvos(18.2));
 
+/**
+ * Read all known attributes from descriptor clusters on all known endpoints.
+ *
+ * @return A dictionary with the paths of the attributes as keys and the
+ *         data-values (as described in the documentation for
+ *         MTRDeviceResponseHandler) as values.
+ */
+- (NSDictionary<MTRAttributePath *, NSDictionary<NSString *, id> *> *)descriptorClusters MTR_NEWLY_AVAILABLE;
+
 /**
  * Invoke a command with a designated command path
  *
diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm
index 7f273e26f03399..4bf71496b0cf1f 100644
--- a/src/darwin/Framework/CHIP/MTRDevice.mm
+++ b/src/darwin/Framework/CHIP/MTRDevice.mm
@@ -363,6 +363,32 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID
     return [NSArray array];
 }
 
+- (NSDictionary<MTRAttributePath *, NSDictionary<NSString *, id> *> *)descriptorClusters
+{
+    @autoreleasepool {
+        // For now, we have a temp array that we should make sure dies as soon
+        // as possible.
+        //
+        // TODO: We should have a version of readAttributePaths that returns a
+        // dictionary in the format we want here.
+        auto path = [MTRAttributeRequestPath requestPathWithEndpointID:nil
+                                                             clusterID:@(MTRClusterIDTypeDescriptorID)
+                                                           attributeID:nil];
+        auto * data = [self readAttributePaths:@[ path ]];
+
+        auto * retval = [NSMutableDictionary dictionaryWithCapacity:data.count];
+        for (NSDictionary * item in data) {
+            // We double-check that the things in our dictionaries are the right
+            // thing, because XPC has no way to check that for us.
+            if (MTR_SAFE_CAST(item[MTRAttributePathKey], MTRAttributePath) && MTR_SAFE_CAST(item[MTRDataKey], NSDictionary)) {
+                retval[item[MTRAttributePathKey]] = item[MTRDataKey];
+            }
+        }
+
+        return retval;
+    }
+}
+
 - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
                           clusterID:(NSNumber *)clusterID
                           commandID:(NSNumber *)commandID
diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m
index 6cabe01cb567ed..729e7a318a9213 100644
--- a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m
+++ b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m
@@ -1588,6 +1588,22 @@ - (void)test017_TestMTRDeviceBasics
     XCTAssertEqualObjects([NSSet setWithArray:variousThings],
         [[NSSet setWithArray:clusterRevisions] setByAddingObjectsFromSet:[NSSet setWithArray:basicInformationAllRootAttributes]]);
 
+    // Quick test for descriptorClusters
+    __auto_type * descriptorPath = [MTRAttributeRequestPath requestPathWithEndpointID:nil clusterID:@(MTRClusterIDTypeDescriptorID) attributeID:nil];
+    __auto_type * descriptorRead = [device descriptorClusters];
+    __auto_type * descriptorWildcardRead = [device readAttributePaths:@[ descriptorPath ]];
+    XCTAssertEqual(descriptorRead.count, descriptorWildcardRead.count);
+    for (MTRAttributePath * path in descriptorRead) {
+        __auto_type * expectedObj = @{
+            MTRAttributePathKey : path,
+            MTRDataKey : descriptorRead[path],
+        };
+        XCTAssertTrue([descriptorWildcardRead containsObject:expectedObj]);
+    }
+    for (NSDictionary * item in descriptorWildcardRead) {
+        XCTAssertEqualObjects(descriptorRead[item[MTRAttributePathKey]], item[MTRDataKey]);
+    }
+
     // Some quick tests for waitForAttributeValues.  First, values that we know
     // are already there:
     XCTestExpectation * deviceTypesWaitExpectation = [self expectationWithDescription:@"deviceTypes is already the value we expect"];