Skip to content

Commit

Permalink
Add better APIs for handling FeatureMap in MTRServerAttribute.
Browse files Browse the repository at this point in the history
Do more sanity checks when using the initializer, and provide a factory method
that makes creating FeatureMap attributes easier.
  • Loading branch information
bzbarsky-apple committed Jun 5, 2024
1 parent 24bbdf4 commit 043bd33
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 2 deletions.
12 changes: 11 additions & 1 deletion src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6))
* Will fail if the attribute ID is not valid per the Matter specification or
* the attribute value is not a valid data-value.
*
* requiredPrivilege is the privilege required to read the attribute.
* requiredPrivilege is the privilege required to read the attribute. This
* initializer may fail if the provided attributeID is a global attribute and
* the provided requiredPrivilege value is not correct for that attribute ID.
*/
- (nullable instancetype)initReadonlyAttributeWithID:(NSNumber *)attributeID initialValue:(NSDictionary<NSString *, id> *)value requiredPrivilege:(MTRAccessControlEntryPrivilege)requiredPrivilege;

Expand All @@ -53,6 +55,14 @@ MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6))
*/
- (BOOL)setValue:(NSDictionary<NSString *, id> *)value;

/**
* Create an attribute description for a FeatureMap attribute with the provided
* value (expected to be an unsigned integer representing the value of the
* bitmap). This will automatically set requiredPrivilege to the right value
* for FeatureMap.
*/
+ (MTRServerAttribute *)newFeatureMapAttributeWithInitialValue:(NSNumber *)value;

@property (atomic, copy, readonly) NSNumber * attributeID;
@property (atomic, copy, readonly) NSDictionary<NSString *, id> * value;
/**
Expand Down
28 changes: 27 additions & 1 deletion src/darwin/Framework/CHIP/ServerEndpoint/MTRServerAttribute.mm
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#import "MTRUnfairLock.h"
#import "NSDataSpanConversion.h"

#import <Matter/MTRClusterConstants.h>
#import <Matter/MTRServerAttribute.h>

#include <app/reporting/reporting.h>
Expand Down Expand Up @@ -57,6 +58,20 @@ - (nullable instancetype)initAttributeWithID:(NSNumber *)attributeID initialValu
return nil;
}

if (attrId == MTRAttributeIDTypeGlobalAttributeFeatureMapID) {
// Some sanity checks: value should be an unsigned-integer NSNumber, and
// requiredReadPrivilege should be View.
if (requiredReadPrivilege != MTRAccessControlEntryPrivilegeView) {
MTR_LOG_ERROR("MTRServerAttribute for FeatureMap provided with invalid read privilege %d", requiredReadPrivilege);
return nil;
}

if (![MTRUnsignedIntegerValueType isEqual:value[MTRTypeKey]]) {
MTR_LOG_ERROR("MTRServerAttribute for FeatureMap provided with value that is not an unsigned integer: %@", value);
return nil;
}
}

return [self initWithAttributeID:[attributeID copy] value:[value copy] requiredReadPrivilege:requiredReadPrivilege writable:writable];
}

Expand All @@ -80,7 +95,8 @@ - (nullable instancetype)initWithAttributeID:(NSNumber *)attributeID value:(NSDi
_writable = writable;
_parentCluster = app::ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);

// Now call setValue to store the value and its serialization.
// now store the value and its serialization. This will also check that the
// value is serializable to TLV.
if ([self setValueInternal:value logIfNotAssociated:NO] == NO) {
return nil;
}
Expand All @@ -93,6 +109,16 @@ - (BOOL)setValue:(NSDictionary<NSString *, id> *)value
return [self setValueInternal:value logIfNotAssociated:YES];
}

+ (MTRServerAttribute *)newFeatureMapAttributeWithInitialValue:(NSNumber *)value
{
return [[MTRServerAttribute alloc] initReadonlyAttributeWithID:@(MTRAttributeIDTypeGlobalAttributeFeatureMapID)
initialValue:@{
MTRTypeKey : MTRUnsignedIntegerValueType,
MTRValueKey : value,
}
requiredPrivilege:MTRAccessControlEntryPrivilegeView];
}

- (BOOL)setValueInternal:(NSDictionary<NSString *, id> *)value logIfNotAssociated:(BOOL)logIfNotAssociated
{
id serializedValue;
Expand Down
35 changes: 35 additions & 0 deletions src/darwin/Framework/CHIPTests/MTRServerEndpointTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ - (void)testServerAttribute
MTRValueKey : @(5),
};

__auto_type * signedIntValue = @{
MTRTypeKey : MTRSignedIntegerValueType,
MTRValueKey : @(5),
};

__auto_type * listOfStringsValue = @{
MTRTypeKey : MTRArrayValueType,
MTRValueKey : @[
Expand Down Expand Up @@ -192,6 +197,36 @@ - (void)testServerAttribute
__auto_type * attr = [[MTRServerAttribute alloc] initReadonlyAttributeWithID:invalidID initialValue:unsignedIntValue requiredPrivilege:MTRAccessControlEntryPrivilegeView];
XCTAssertNil(attr);
}

// Valid FeatureMap attribute
{
__auto_type * attr = [[MTRServerAttribute alloc] initReadonlyAttributeWithID:@(MTRAttributeIDTypeGlobalAttributeFeatureMapID) initialValue:unsignedIntValue requiredPrivilege:MTRAccessControlEntryPrivilegeView];
XCTAssertNotNil(attr);
XCTAssertEqualObjects(attr.attributeID, @(MTRAttributeIDTypeGlobalAttributeFeatureMapID));
XCTAssertEqualObjects(attr.value, unsignedIntValue);
XCTAssertEqual(attr.writable, NO);
}

// FeatureMap attribute with wrong value type
{
__auto_type * attr = [[MTRServerAttribute alloc] initReadonlyAttributeWithID:@(MTRAttributeIDTypeGlobalAttributeFeatureMapID) initialValue:signedIntValue requiredPrivilege:MTRAccessControlEntryPrivilegeView];
XCTAssertNil(attr);
}

// FeatureMap attribute with wrong permissions
{
__auto_type * attr = [[MTRServerAttribute alloc] initReadonlyAttributeWithID:@(MTRAttributeIDTypeGlobalAttributeFeatureMapID) initialValue:unsignedIntValue requiredPrivilege:MTRAccessControlEntryPrivilegeOperate];
XCTAssertNil(attr);
}

// FeatureMap attribute via factory
{
__auto_type * attr = [MTRServerAttribute newFeatureMapAttributeWithInitialValue:@(5)];
XCTAssertNotNil(attr);
XCTAssertEqualObjects(attr.attributeID, @(MTRAttributeIDTypeGlobalAttributeFeatureMapID));
XCTAssertEqualObjects(attr.value, unsignedIntValue);
XCTAssertEqual(attr.writable, NO);
}
}

- (void)testDeviceType
Expand Down

0 comments on commit 043bd33

Please sign in to comment.