Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a wildcard attribute read API on MTRDevice. #35562

Merged
merged 6 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/darwin/Framework/CHIP/MTRBaseDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ typedef NS_ENUM(uint8_t, MTRTransportType) {
*/
NS_SWIFT_SENDABLE
MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0))
@interface MTRAttributeRequestPath : NSObject <NSCopying>
@interface MTRAttributeRequestPath : NSObject <NSCopying, NSSecureCoding>
@property (nonatomic, readonly, copy, nullable) NSNumber * endpoint MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0));
@property (nonatomic, readonly, copy, nullable) NSNumber * cluster MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0));
@property (nonatomic, readonly, copy, nullable)
Expand All @@ -185,7 +185,7 @@ MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0))
*/
NS_SWIFT_SENDABLE
MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0))
@interface MTREventRequestPath : NSObject <NSCopying>
@interface MTREventRequestPath : NSObject <NSCopying, NSSecureCoding>
@property (nonatomic, readonly, copy, nullable) NSNumber * endpoint MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0));
@property (nonatomic, readonly, copy, nullable) NSNumber * cluster MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0));
@property (nonatomic, readonly, copy, nullable) NSNumber * event MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0));
Expand Down
101 changes: 101 additions & 0 deletions src/darwin/Framework/CHIP/MTRBaseDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2333,6 +2333,57 @@ - (void)convertToAttributePathParams:(chip::app::AttributePathParams &)params
params.SetWildcardAttributeId();
}
}

static NSString * const sEndpointIDKey = @"endpointIDKey";
static NSString * const sClusterIDKey = @"clusterIDKey";
static NSString * const sAttributeIDKey = @"attributeIDKey";

+ (BOOL)supportsSecureCoding
{
return YES;
}

- (nullable instancetype)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if (self == nil) {
return nil;
}

_endpoint = [decoder decodeObjectOfClass:[NSNumber class] forKey:sEndpointIDKey];
if (_endpoint && ![_endpoint isKindOfClass:[NSNumber class]]) {
MTR_LOG_ERROR("MTRAttributeRequestPath decoded %@ for endpoint, not NSNumber.", _attribute);
return nil;
}

_cluster = [decoder decodeObjectOfClass:[NSNumber class] forKey:sClusterIDKey];
if (_cluster && ![_cluster isKindOfClass:[NSNumber class]]) {
MTR_LOG_ERROR("MTRAttributeRequestPath decoded %@ for cluster, not NSNumber.", _attribute);
return nil;
}

_attribute = [decoder decodeObjectOfClass:[NSNumber class] forKey:sAttributeIDKey];
if (_attribute && ![_attribute isKindOfClass:[NSNumber class]]) {
MTR_LOG_ERROR("MTRAttributeRequestPath decoded %@ for attribute, not NSNumber.", _attribute);
return nil;
}

return self;
}

- (void)encodeWithCoder:(NSCoder *)coder
{
if (_endpoint) {
[coder encodeObject:_endpoint forKey:sEndpointIDKey];
}
if (_cluster) {
[coder encodeObject:_cluster forKey:sClusterIDKey];
}
if (_attribute) {
[coder encodeObject:_attribute forKey:sAttributeIDKey];
}
}

@end

@implementation MTREventRequestPath
Expand Down Expand Up @@ -2406,6 +2457,56 @@ - (void)convertToEventPathParams:(chip::app::EventPathParams &)params
params.SetWildcardEventId();
}
}
static NSString * const sEventEndpointIDKey = @"endpointIDKey";
static NSString * const sEventClusterIDKey = @"clusterIDKey";
static NSString * const sEventAttributeIDKey = @"attributeIDKey";

+ (BOOL)supportsSecureCoding
{
return YES;
}

- (nullable instancetype)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if (self == nil) {
return nil;
}

_endpoint = [decoder decodeObjectOfClass:[NSNumber class] forKey:sEventEndpointIDKey];
if (_endpoint && ![_endpoint isKindOfClass:[NSNumber class]]) {
MTR_LOG_ERROR("MTREventRequestPath decoded %@ for endpoint, not NSNumber.", _endpoint);
return nil;
}

_cluster = [decoder decodeObjectOfClass:[NSNumber class] forKey:sEventClusterIDKey];
if (_cluster && ![_cluster isKindOfClass:[NSNumber class]]) {
MTR_LOG_ERROR("MTREventRequestPath decoded %@ for cluster, not NSNumber.", _cluster);
return nil;
}

_event = [decoder decodeObjectOfClass:[NSNumber class] forKey:sEventAttributeIDKey];
if (_event && ![_event isKindOfClass:[NSNumber class]]) {
MTR_LOG_ERROR("MTREventRequestPath decoded %@ for event, not NSNumber.", _event);
return nil;
}

return self;
}

- (void)encodeWithCoder:(NSCoder *)coder
{
if (_endpoint) {
[coder encodeObject:_endpoint forKey:sEventEndpointIDKey];
}
if (_cluster) {
[coder encodeObject:_cluster forKey:sEventClusterIDKey];
}
if (_event) {
[coder encodeObject:_event forKey:sEventAttributeIDKey];
}
}

@end

@implementation MTRClusterPath
Expand Down
14 changes: 14 additions & 0 deletions src/darwin/Framework/CHIP/MTRDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,20 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1))
expectedValueInterval:(NSNumber *)expectedValueInterval
timedWriteTimeout:(NSNumber * _Nullable)timeout;

/**
* Read the attributes identified by the provided attribute paths. The paths
* can include wildcards.
*
* Paths that do not correspond to any existing attributes, or that the
* MTRDevice does not have attribute values for, will not be present in the
* return value from this function.
*
* @return an array of response-value dictionaries as described in the
* documentation for MTRDeviceResponseHandler. Each one will have an
* MTRAttributePathKey and an MTRDataKey.
*/
- (NSArray<NSDictionary<NSString *, id> *> *)readAttributePaths:(NSArray<MTRAttributeRequestPath *> *)attributePaths MTR_NEWLY_AVAILABLE;

/**
* Invoke a command with a designated command path
*
Expand Down
11 changes: 11 additions & 0 deletions src/darwin/Framework/CHIP/MTRDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,17 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID
#undef MTRDeviceErrorStr
}

- (NSArray<NSDictionary<NSString *, id> *> *)readAttributePaths:(NSArray<MTRAttributeRequestPath *> *)attributePaths
{
#define MTRDeviceErrorStr "MTRDevice readAttributePaths: must be handled by subclasses"
MTR_LOG_ERROR(MTRDeviceErrorStr);
#ifdef DEBUG
NSAssert(NO, @MTRDeviceErrorStr);
#endif // DEBUG
#undef MTRDeviceErrorStr
return [NSArray array];
}

- (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
clusterID:(NSNumber *)clusterID
commandID:(NSNumber *)commandID
Expand Down
10 changes: 7 additions & 3 deletions src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ - (NSXPCInterface *)_interfaceForServerProtocol
NSMutableSet * allowedClasses = [MTRDeviceController_XPC _allowedClasses];
[allowedClasses addObjectsFromArray:@[
[MTRCommandPath class],
[MTRAttributePath class]
[MTRAttributePath class],
[MTRAttributeRequestPath class],
[MTREventRequestPath class]
]];

[interface setClasses:allowedClasses
Expand All @@ -82,7 +84,8 @@ - (NSXPCInterface *)_interfaceForClientProtocol
NSXPCInterface * interface = [NSXPCInterface interfaceWithProtocol:@protocol(MTRXPCClientProtocol)];
NSMutableSet * allowedClasses = [MTRDeviceController_XPC _allowedClasses];
[allowedClasses addObjectsFromArray:@[
[MTRAttributePath class]
[MTRAttributePath class],
[MTRAttributeRequestPath class]
woody-apple marked this conversation as resolved.
Show resolved Hide resolved
]];

[interface setClasses:allowedClasses
Expand All @@ -92,7 +95,8 @@ - (NSXPCInterface *)_interfaceForClientProtocol

allowedClasses = [MTRDeviceController_XPC _allowedClasses];
[allowedClasses addObjectsFromArray:@[
[MTREventPath class]
[MTREventPath class],
[MTREventRequestPath class]
woody-apple marked this conversation as resolved.
Show resolved Hide resolved
]];

[interface setClasses:allowedClasses
Expand Down
129 changes: 119 additions & 10 deletions src/darwin/Framework/CHIP/MTRDevice_Concrete.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2949,6 +2949,79 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID
[_asyncWorkQueue enqueueWorkItem:workItem descriptionWithFormat:@"write %@ 0x%llx 0x%llx", endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue];
}

- (NSArray<NSDictionary<NSString *, id> *> *)readAttributePaths:(NSArray<MTRAttributeRequestPath *> *)attributePaths
{
// 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.
NSMutableSet<MTRAttributePath *> * existentPaths = [[NSMutableSet alloc] init];
{
std::lock_guard lock(_lock);
for (MTRAttributeRequestPath * path in attributePaths) {
[self _addExistentPathsFor:path to:existentPaths];
}
}

NSMutableArray<NSDictionary<NSString *, id> *> * result = [NSMutableArray arrayWithCapacity:existentPaths.count];
for (MTRAttributePath * path in existentPaths) {
auto * value = [self readAttributeWithEndpointID:path.endpoint clusterID:path.cluster attributeID:path.attribute params:nil];
if (!value) {
continue;
}
[result addObject:@{
MTRAttributePathKey : path,
MTRDataKey : value,
}];
}

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
Expand Down Expand Up @@ -3954,19 +4027,55 @@ - (void)_updateAttributeDependentDescriptionData
{
os_unfair_lock_assert_owner(&_lock);

auto * partsListPath = [MTRAttributePath attributePathWithEndpointID:@(kRootEndpointId)
clusterID:@(MTRClusterIDTypeDescriptorID)
attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributePartsListID)];
auto * partsList = [self _cachedAttributeValueForPath:partsListPath];
NSMutableArray<NSNumber *> * endpointsOnDevice = [self arrayOfNumbersFromAttributeValue:partsList];
if (!endpointsOnDevice) {
endpointsOnDevice = [[NSMutableArray<NSNumber *> alloc] init];
}
// Add Root node!
[endpointsOnDevice addObject:@(0)];
auto * partsList = [self _cachedListOfNumbersValueForEndpointID:@(kRootEndpointId)
clusterID:@(MTRClusterIDTypeDescriptorID)
attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributePartsListID)];
NSMutableArray<NSNumber *> * endpointsOnDevice = [partsList mutableCopy];
// Add Root Node endpoint.
[endpointsOnDevice addObject:@(kRootEndpointId)];
return endpointsOnDevice;
}

/**
* Returns the cached value of the relevant attribute as a list of numbers.
* Returns an empty list if the value does not exist or can't be converted to a
* list of numbers.
*/
- (NSArray<NSNumber *> *)_cachedListOfNumbersValueForEndpointID:(NSNumber *)endpointID
clusterID:(NSNumber *)clusterID
attributeID:(NSNumber *)attributeID
{
os_unfair_lock_assert_owner(&_lock);

auto * path = [MTRAttributePath attributePathWithEndpointID:endpointID
clusterID:clusterID
attributeID:attributeID];
auto * value = [self _cachedAttributeValueForPath:path];
NSArray<NSNumber *> * arrayValue = [self arrayOfNumbersFromAttributeValue:value];
if (arrayValue) {
return arrayValue;
}
return [NSArray array];
}

- (NSArray<NSNumber *> *)_serverListForEndpointID:(NSNumber *)endpointID
{
os_unfair_lock_assert_owner(&_lock);

return [self _cachedListOfNumbersValueForEndpointID:endpointID
clusterID:@(MTRClusterIDTypeDescriptorID)
attributeID:@(MTRAttributeIDTypeClusterDescriptorAttributeServerListID)];
}

- (NSArray<NSNumber *> *)_attributeListForEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID
{
os_unfair_lock_assert_owner(&_lock);

return [self _cachedListOfNumbersValueForEndpointID:endpointID
clusterID:clusterID
attributeID:@(MTRAttributeIDTypeGlobalAttributeAttributeListID)];
}

- (NSNumber * _Nullable)_networkFeatures
{
NSNumber * _Nullable result = nil;
Expand Down
2 changes: 2 additions & 0 deletions src/darwin/Framework/CHIP/MTRDevice_XPC.mm
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ - (oneway void)device:(NSNumber *)nodeID internalStateUpdated:(NSDictionary *)di
: expectedValueInterval timedWriteTimeout
: timeout)

MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(readAttributePaths, NSArray<MTRAttributeRequestPath *> *, nil, readAttributePathsWithReply)

- (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID
clusterID:(NSNumber *)clusterID
commandID:(NSNumber *)commandID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ MTR_NEWLY_AVAILABLE
- (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID getDeviceCachePrimedWithReply:(void (^)(BOOL primed))reply;
- (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID getEstimatedStartTimeWithReply:(void (^)(NSDate * _Nullable estimatedStartTime))reply;
- (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID getEstimatedSubscriptionLatencyWithReply:(void (^)(NSNumber * _Nullable estimatedSubscriptionLatency))reply;
- (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID readAttributePathsWithReply:(void (^)(NSArray<MTRAttributeRequestPath *> * attributesPaths))reply;

- (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID readAttributeWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID params:(MTRReadParams * _Nullable)params withReply:(void (^)(NSDictionary<NSString *, id> * _Nullable))reply;
- (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID writeAttributeWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID value:(id)value expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval timedWriteTimeout:(NSNumber * _Nullable)timeout;
Expand Down
Loading
Loading