Skip to content

Commit f78dad4

Browse files
bzbarsky-applewoody-applerestyled-commits
authored
Add a wildcard attribute read API on MTRDevice. (project-chip#35562)
* Add a wildcard attribute read API on MTRDevice. * Adding new XPC protocol * Restyled by whitespace * Restyled by clang-format * Add unit test for NSCoding bits. * Fix up XPC protocol bits. --------- Co-authored-by: Justin Wood <woody@apple.com> Co-authored-by: Restyled.io <commits@restyled.io>
1 parent 16e4e9b commit f78dad4

9 files changed

+362
-47
lines changed

src/darwin/Framework/CHIP/MTRBaseDevice.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ typedef NS_ENUM(uint8_t, MTRTransportType) {
166166
*/
167167
NS_SWIFT_SENDABLE
168168
MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0))
169-
@interface MTRAttributeRequestPath : NSObject <NSCopying>
169+
@interface MTRAttributeRequestPath : NSObject <NSCopying, NSSecureCoding>
170170
@property (nonatomic, readonly, copy, nullable) NSNumber * endpoint MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0));
171171
@property (nonatomic, readonly, copy, nullable) NSNumber * cluster MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0));
172172
@property (nonatomic, readonly, copy, nullable)
@@ -185,7 +185,7 @@ MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0))
185185
*/
186186
NS_SWIFT_SENDABLE
187187
MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0))
188-
@interface MTREventRequestPath : NSObject <NSCopying>
188+
@interface MTREventRequestPath : NSObject <NSCopying, NSSecureCoding>
189189
@property (nonatomic, readonly, copy, nullable) NSNumber * endpoint MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0));
190190
@property (nonatomic, readonly, copy, nullable) NSNumber * cluster MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0));
191191
@property (nonatomic, readonly, copy, nullable) NSNumber * event MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0));

src/darwin/Framework/CHIP/MTRBaseDevice.mm

+101
Original file line numberDiff line numberDiff line change
@@ -2333,6 +2333,57 @@ - (void)convertToAttributePathParams:(chip::app::AttributePathParams &)params
23332333
params.SetWildcardAttributeId();
23342334
}
23352335
}
2336+
2337+
static NSString * const sEndpointIDKey = @"endpointIDKey";
2338+
static NSString * const sClusterIDKey = @"clusterIDKey";
2339+
static NSString * const sAttributeIDKey = @"attributeIDKey";
2340+
2341+
+ (BOOL)supportsSecureCoding
2342+
{
2343+
return YES;
2344+
}
2345+
2346+
- (nullable instancetype)initWithCoder:(NSCoder *)decoder
2347+
{
2348+
self = [super init];
2349+
if (self == nil) {
2350+
return nil;
2351+
}
2352+
2353+
_endpoint = [decoder decodeObjectOfClass:[NSNumber class] forKey:sEndpointIDKey];
2354+
if (_endpoint && ![_endpoint isKindOfClass:[NSNumber class]]) {
2355+
MTR_LOG_ERROR("MTRAttributeRequestPath decoded %@ for endpoint, not NSNumber.", _attribute);
2356+
return nil;
2357+
}
2358+
2359+
_cluster = [decoder decodeObjectOfClass:[NSNumber class] forKey:sClusterIDKey];
2360+
if (_cluster && ![_cluster isKindOfClass:[NSNumber class]]) {
2361+
MTR_LOG_ERROR("MTRAttributeRequestPath decoded %@ for cluster, not NSNumber.", _attribute);
2362+
return nil;
2363+
}
2364+
2365+
_attribute = [decoder decodeObjectOfClass:[NSNumber class] forKey:sAttributeIDKey];
2366+
if (_attribute && ![_attribute isKindOfClass:[NSNumber class]]) {
2367+
MTR_LOG_ERROR("MTRAttributeRequestPath decoded %@ for attribute, not NSNumber.", _attribute);
2368+
return nil;
2369+
}
2370+
2371+
return self;
2372+
}
2373+
2374+
- (void)encodeWithCoder:(NSCoder *)coder
2375+
{
2376+
if (_endpoint) {
2377+
[coder encodeObject:_endpoint forKey:sEndpointIDKey];
2378+
}
2379+
if (_cluster) {
2380+
[coder encodeObject:_cluster forKey:sClusterIDKey];
2381+
}
2382+
if (_attribute) {
2383+
[coder encodeObject:_attribute forKey:sAttributeIDKey];
2384+
}
2385+
}
2386+
23362387
@end
23372388

23382389
@implementation MTREventRequestPath
@@ -2406,6 +2457,56 @@ - (void)convertToEventPathParams:(chip::app::EventPathParams &)params
24062457
params.SetWildcardEventId();
24072458
}
24082459
}
2460+
static NSString * const sEventEndpointIDKey = @"endpointIDKey";
2461+
static NSString * const sEventClusterIDKey = @"clusterIDKey";
2462+
static NSString * const sEventAttributeIDKey = @"attributeIDKey";
2463+
2464+
+ (BOOL)supportsSecureCoding
2465+
{
2466+
return YES;
2467+
}
2468+
2469+
- (nullable instancetype)initWithCoder:(NSCoder *)decoder
2470+
{
2471+
self = [super init];
2472+
if (self == nil) {
2473+
return nil;
2474+
}
2475+
2476+
_endpoint = [decoder decodeObjectOfClass:[NSNumber class] forKey:sEventEndpointIDKey];
2477+
if (_endpoint && ![_endpoint isKindOfClass:[NSNumber class]]) {
2478+
MTR_LOG_ERROR("MTREventRequestPath decoded %@ for endpoint, not NSNumber.", _endpoint);
2479+
return nil;
2480+
}
2481+
2482+
_cluster = [decoder decodeObjectOfClass:[NSNumber class] forKey:sEventClusterIDKey];
2483+
if (_cluster && ![_cluster isKindOfClass:[NSNumber class]]) {
2484+
MTR_LOG_ERROR("MTREventRequestPath decoded %@ for cluster, not NSNumber.", _cluster);
2485+
return nil;
2486+
}
2487+
2488+
_event = [decoder decodeObjectOfClass:[NSNumber class] forKey:sEventAttributeIDKey];
2489+
if (_event && ![_event isKindOfClass:[NSNumber class]]) {
2490+
MTR_LOG_ERROR("MTREventRequestPath decoded %@ for event, not NSNumber.", _event);
2491+
return nil;
2492+
}
2493+
2494+
return self;
2495+
}
2496+
2497+
- (void)encodeWithCoder:(NSCoder *)coder
2498+
{
2499+
if (_endpoint) {
2500+
[coder encodeObject:_endpoint forKey:sEventEndpointIDKey];
2501+
}
2502+
if (_cluster) {
2503+
[coder encodeObject:_cluster forKey:sEventClusterIDKey];
2504+
}
2505+
if (_event) {
2506+
[coder encodeObject:_event forKey:sEventAttributeIDKey];
2507+
}
2508+
}
2509+
24092510
@end
24102511

24112512
@implementation MTRClusterPath

src/darwin/Framework/CHIP/MTRDevice.h

+14
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,20 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1))
186186
expectedValueInterval:(NSNumber *)expectedValueInterval
187187
timedWriteTimeout:(NSNumber * _Nullable)timeout;
188188

189+
/**
190+
* Read the attributes identified by the provided attribute paths. The paths
191+
* can include wildcards.
192+
*
193+
* Paths that do not correspond to any existing attributes, or that the
194+
* MTRDevice does not have attribute values for, will not be present in the
195+
* return value from this function.
196+
*
197+
* @return an array of response-value dictionaries as described in the
198+
* documentation for MTRDeviceResponseHandler. Each one will have an
199+
* MTRAttributePathKey and an MTRDataKey.
200+
*/
201+
- (NSArray<NSDictionary<NSString *, id> *> *)readAttributePaths:(NSArray<MTRAttributeRequestPath *> *)attributePaths MTR_NEWLY_AVAILABLE;
202+
189203
/**
190204
* Invoke a command with a designated command path
191205
*

src/darwin/Framework/CHIP/MTRDevice.mm

+11
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,17 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID
12721272
#undef MTRDeviceErrorStr
12731273
}
12741274

1275+
- (NSArray<NSDictionary<NSString *, id> *> *)readAttributePaths:(NSArray<MTRAttributeRequestPath *> *)attributePaths
1276+
{
1277+
#define MTRDeviceErrorStr "MTRDevice readAttributePaths: must be handled by subclasses"
1278+
MTR_LOG_ERROR(MTRDeviceErrorStr);
1279+
#ifdef DEBUG
1280+
NSAssert(NO, @MTRDeviceErrorStr);
1281+
#endif // DEBUG
1282+
#undef MTRDeviceErrorStr
1283+
return [NSArray array];
1284+
}
1285+
12751286
- (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
12761287
clusterID:(NSNumber *)clusterID
12771288
commandID:(NSNumber *)commandID

src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm

+24-3
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,35 @@ - (NSXPCInterface *)_interfaceForServerProtocol
6666
NSMutableSet * allowedClasses = [MTRDeviceController_XPC _allowedClasses];
6767
[allowedClasses addObjectsFromArray:@[
6868
[MTRCommandPath class],
69-
[MTRAttributePath class]
69+
[MTRAttributePath class],
7070
]];
7171

7272
[interface setClasses:allowedClasses
7373
forSelector:@selector(deviceController:nodeID:invokeCommandWithEndpointID:clusterID:commandID:commandFields:expectedValues:expectedValueInterval:timedInvokeTimeout:serverSideProcessingTimeout:completion:)
7474
argumentIndex:0
7575
ofReply:YES];
7676

77+
// readAttributePaths: gets handed an array of MTRAttributeRequestPath.
78+
allowedClasses = [MTRDeviceController_XPC _allowedClasses];
79+
[allowedClasses addObjectsFromArray:@[
80+
[MTRAttributeRequestPath class],
81+
]];
82+
[interface setClasses:allowedClasses
83+
forSelector:@selector(deviceController:nodeID:readAttributePaths:withReply:)
84+
argumentIndex:2
85+
ofReply:NO];
86+
87+
// readAttributePaths: returns response-value dictionaries that have
88+
// attribute paths and values.
89+
allowedClasses = [MTRDeviceController_XPC _allowedClasses];
90+
[allowedClasses addObjectsFromArray:@[
91+
[MTRAttributePath class],
92+
]];
93+
[interface setClasses:allowedClasses
94+
forSelector:@selector(deviceController:nodeID:readAttributePaths:withReply:)
95+
argumentIndex:0
96+
ofReply:YES];
97+
7798
return interface;
7899
}
79100

@@ -82,7 +103,7 @@ - (NSXPCInterface *)_interfaceForClientProtocol
82103
NSXPCInterface * interface = [NSXPCInterface interfaceWithProtocol:@protocol(MTRXPCClientProtocol)];
83104
NSMutableSet * allowedClasses = [MTRDeviceController_XPC _allowedClasses];
84105
[allowedClasses addObjectsFromArray:@[
85-
[MTRAttributePath class]
106+
[MTRAttributePath class],
86107
]];
87108

88109
[interface setClasses:allowedClasses
@@ -92,7 +113,7 @@ - (NSXPCInterface *)_interfaceForClientProtocol
92113

93114
allowedClasses = [MTRDeviceController_XPC _allowedClasses];
94115
[allowedClasses addObjectsFromArray:@[
95-
[MTREventPath class]
116+
[MTREventPath class],
96117
]];
97118

98119
[interface setClasses:allowedClasses

src/darwin/Framework/CHIP/MTRDevice_Concrete.mm

+119-10
Original file line numberDiff line numberDiff line change
@@ -2963,6 +2963,79 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID
29632963
[_asyncWorkQueue enqueueWorkItem:workItem descriptionWithFormat:@"write %@ 0x%llx 0x%llx", endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue];
29642964
}
29652965

2966+
- (NSArray<NSDictionary<NSString *, id> *> *)readAttributePaths:(NSArray<MTRAttributeRequestPath *> *)attributePaths
2967+
{
2968+
// Determine the set of what the spec calls "existent paths" that correspond
2969+
// to the request paths. Building the whole set in-memory is OK, because
2970+
// we're going to need all those paths for our return value anyway.
2971+
NSMutableSet<MTRAttributePath *> * existentPaths = [[NSMutableSet alloc] init];
2972+
{
2973+
std::lock_guard lock(_lock);
2974+
for (MTRAttributeRequestPath * path in attributePaths) {
2975+
[self _addExistentPathsFor:path to:existentPaths];
2976+
}
2977+
}
2978+
2979+
NSMutableArray<NSDictionary<NSString *, id> *> * result = [NSMutableArray arrayWithCapacity:existentPaths.count];
2980+
for (MTRAttributePath * path in existentPaths) {
2981+
auto * value = [self readAttributeWithEndpointID:path.endpoint clusterID:path.cluster attributeID:path.attribute params:nil];
2982+
if (!value) {
2983+
continue;
2984+
}
2985+
[result addObject:@{
2986+
MTRAttributePathKey : path,
2987+
MTRDataKey : value,
2988+
}];
2989+
}
2990+
2991+
return result;
2992+
}
2993+
2994+
- (void)_addExistentPathsFor:(MTRAttributeRequestPath *)path to:(NSMutableSet<MTRAttributePath *> *)set
2995+
{
2996+
os_unfair_lock_assert_owner(&_lock);
2997+
2998+
if (path.endpoint != nil) {
2999+
[self _addExistentPathsForEndpoint:path.endpoint path:path to:set];
3000+
return;
3001+
}
3002+
3003+
NSArray<NSNumber *> * endpointList = [self _endpointList];
3004+
for (NSNumber * endpoint in endpointList) {
3005+
[self _addExistentPathsForEndpoint:endpoint path:path to:set];
3006+
}
3007+
}
3008+
3009+
- (void)_addExistentPathsForEndpoint:(NSNumber *)endpoint path:(MTRAttributeRequestPath *)path to:(NSMutableSet<MTRAttributePath *> *)set
3010+
{
3011+
os_unfair_lock_assert_owner(&_lock);
3012+
3013+
if (path.cluster != nil) {
3014+
[self _addExistentPathsForEndpoint:endpoint cluster:path.cluster attribute:path.attribute to:set];
3015+
return;
3016+
}
3017+
3018+
auto * clusterList = [self _serverListForEndpointID:endpoint];
3019+
for (NSNumber * cluster in clusterList) {
3020+
[self _addExistentPathsForEndpoint:endpoint cluster:cluster attribute:path.attribute to:set];
3021+
}
3022+
}
3023+
3024+
- (void)_addExistentPathsForEndpoint:(NSNumber *)endpoint cluster:(NSNumber *)cluster attribute:(NSNumber * _Nullable)attribute to:(NSMutableSet<MTRAttributePath *> *)set
3025+
{
3026+
os_unfair_lock_assert_owner(&_lock);
3027+
3028+
if (attribute != nil) {
3029+
[set addObject:[MTRAttributePath attributePathWithEndpointID:endpoint clusterID:cluster attributeID:attribute]];
3030+
return;
3031+
}
3032+
3033+
auto * attributeList = [self _attributeListForEndpointID:endpoint clusterID:cluster];
3034+
for (NSNumber * existentAttribute in attributeList) {
3035+
[set addObject:[MTRAttributePath attributePathWithEndpointID:endpoint clusterID:cluster attributeID:existentAttribute]];
3036+
}
3037+
}
3038+
29663039
- (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID
29673040
clusterID:(NSNumber *)clusterID
29683041
commandID:(NSNumber *)commandID
@@ -3968,19 +4041,55 @@ - (void)_updateAttributeDependentDescriptionData
39684041
{
39694042
os_unfair_lock_assert_owner(&_lock);
39704043

3971-
auto * partsListPath = [MTRAttributePath attributePathWithEndpointID:@(kRootEndpointId)
3972-
clusterID:@(MTRClusterIDTypeDescriptorID)
3973-
attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributePartsListID)];
3974-
auto * partsList = [self _cachedAttributeValueForPath:partsListPath];
3975-
NSMutableArray<NSNumber *> * endpointsOnDevice = [self arrayOfNumbersFromAttributeValue:partsList];
3976-
if (!endpointsOnDevice) {
3977-
endpointsOnDevice = [[NSMutableArray<NSNumber *> alloc] init];
3978-
}
3979-
// Add Root node!
3980-
[endpointsOnDevice addObject:@(0)];
4044+
auto * partsList = [self _cachedListOfNumbersValueForEndpointID:@(kRootEndpointId)
4045+
clusterID:@(MTRClusterIDTypeDescriptorID)
4046+
attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributePartsListID)];
4047+
NSMutableArray<NSNumber *> * endpointsOnDevice = [partsList mutableCopy];
4048+
// Add Root Node endpoint.
4049+
[endpointsOnDevice addObject:@(kRootEndpointId)];
39814050
return endpointsOnDevice;
39824051
}
39834052

4053+
/**
4054+
* Returns the cached value of the relevant attribute as a list of numbers.
4055+
* Returns an empty list if the value does not exist or can't be converted to a
4056+
* list of numbers.
4057+
*/
4058+
- (NSArray<NSNumber *> *)_cachedListOfNumbersValueForEndpointID:(NSNumber *)endpointID
4059+
clusterID:(NSNumber *)clusterID
4060+
attributeID:(NSNumber *)attributeID
4061+
{
4062+
os_unfair_lock_assert_owner(&_lock);
4063+
4064+
auto * path = [MTRAttributePath attributePathWithEndpointID:endpointID
4065+
clusterID:clusterID
4066+
attributeID:attributeID];
4067+
auto * value = [self _cachedAttributeValueForPath:path];
4068+
NSArray<NSNumber *> * arrayValue = [self arrayOfNumbersFromAttributeValue:value];
4069+
if (arrayValue) {
4070+
return arrayValue;
4071+
}
4072+
return [NSArray array];
4073+
}
4074+
4075+
- (NSArray<NSNumber *> *)_serverListForEndpointID:(NSNumber *)endpointID
4076+
{
4077+
os_unfair_lock_assert_owner(&_lock);
4078+
4079+
return [self _cachedListOfNumbersValueForEndpointID:endpointID
4080+
clusterID:@(MTRClusterIDTypeDescriptorID)
4081+
attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributeServerListID)];
4082+
}
4083+
4084+
- (NSArray<NSNumber *> *)_attributeListForEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID
4085+
{
4086+
os_unfair_lock_assert_owner(&_lock);
4087+
4088+
return [self _cachedListOfNumbersValueForEndpointID:endpointID
4089+
clusterID:clusterID
4090+
attributeID:@(MTRAttributeIDTypeGlobalAttributeAttributeListID)];
4091+
}
4092+
39844093
- (NSNumber * _Nullable)_networkFeatures
39854094
{
39864095
NSNumber * _Nullable result = nil;

0 commit comments

Comments
 (0)