diff --git a/src/app/AttributePathParams.h b/src/app/AttributePathParams.h index 52d5bb5972162b..f69340aa3a7ea1 100644 --- a/src/app/AttributePathParams.h +++ b/src/app/AttributePathParams.h @@ -30,7 +30,7 @@ struct AttributePathParams { AttributePathParams() = default; - explicit AttributePathParams(EndpointId aEndpointId) : + constexpr explicit AttributePathParams(EndpointId aEndpointId) : AttributePathParams(aEndpointId, kInvalidClusterId, kInvalidAttributeId, kInvalidListIndex) {} @@ -38,19 +38,19 @@ struct AttributePathParams // TODO: (Issue #10596) Need to ensure that we do not encode the NodeId over the wire // if it is either not 'set', or is set to a value that matches accessing fabric // on which the interaction is undertaken. - AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId) : + constexpr AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId) : AttributePathParams(aEndpointId, aClusterId, kInvalidAttributeId, kInvalidListIndex) {} - AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId, AttributeId aAttributeId) : + constexpr AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId, AttributeId aAttributeId) : AttributePathParams(aEndpointId, aClusterId, aAttributeId, kInvalidListIndex) {} - AttributePathParams(ClusterId aClusterId, AttributeId aAttributeId) : + constexpr AttributePathParams(ClusterId aClusterId, AttributeId aAttributeId) : AttributePathParams(kInvalidEndpointId, aClusterId, aAttributeId, kInvalidListIndex) {} - AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId, AttributeId aAttributeId, ListIndex aListIndex) : + constexpr AttributePathParams(EndpointId aEndpointId, ClusterId aClusterId, AttributeId aAttributeId, ListIndex aListIndex) : mClusterId(aClusterId), mAttributeId(aAttributeId), mEndpointId(aEndpointId), mListIndex(aListIndex) {} diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp index e9dac8df879bdb..d156d3058272d2 100644 --- a/src/controller/AutoCommissioner.cpp +++ b/src/controller/AutoCommissioner.cpp @@ -18,13 +18,14 @@ #include -#include - #include #include #include #include +#include +#include + namespace chip { namespace Controller { @@ -225,6 +226,32 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam mParams.SetICDClientType(params.GetICDClientType().Value()); } + auto extraReadPaths = params.GetExtraReadPaths(); + if (extraReadPaths.size() > 0) + { + using ReadPath = std::remove_pointer_t; + static_assert(std::is_trivially_copyable_v, "can't use memmove / memcpy, not trivially copyable"); + + if (mExtraReadPaths.AllocatedSize() == extraReadPaths.size()) + { + memmove(mExtraReadPaths.Get(), extraReadPaths.data(), extraReadPaths.size() * sizeof(ReadPath)); + } + else + { + // We can't reallocate mExtraReadPaths yet as this would free the old buffer, + // and the caller might be passing a sub-span of the old paths. + decltype(mExtraReadPaths) oldReadPaths(std::move(mExtraReadPaths)); + VerifyOrReturnError(mExtraReadPaths.Alloc(extraReadPaths.size()), CHIP_ERROR_NO_MEMORY); + memcpy(mExtraReadPaths.Get(), extraReadPaths.data(), extraReadPaths.size() * sizeof(ReadPath)); + } + + mParams.SetExtraReadPaths(mExtraReadPaths.Span()); + } + else + { + mExtraReadPaths.Free(); + } + return CHIP_NO_ERROR; } diff --git a/src/controller/AutoCommissioner.h b/src/controller/AutoCommissioner.h index deacda6e3cca31..1589da4225850f 100644 --- a/src/controller/AutoCommissioner.h +++ b/src/controller/AutoCommissioner.h @@ -16,10 +16,13 @@ * limitations under the License. */ #pragma once + +#include #include #include #include #include +#include #include namespace chip { @@ -116,7 +119,9 @@ class AutoCommissioner : public CommissioningDelegate CommissioneeDeviceProxy * mCommissioneeDeviceProxy = nullptr; OperationalCredentialsDelegate * mOperationalCredentialsDelegate = nullptr; OperationalDeviceProxy mOperationalDeviceProxy; - // Memory space for the commisisoning parameters that come in as ByteSpans - the caller is not guaranteed to retain this memory + + // BEGIN memory space for commissioning parameters that are Spans or similar pointers: + // The caller is not guaranteed to retain the memory these parameters point to. uint8_t mSsid[CommissioningParameters::kMaxSsidLen]; uint8_t mCredentials[CommissioningParameters::kMaxCredentialsLen]; uint8_t mThreadOperationalDataset[CommissioningParameters::kMaxThreadDatasetLen]; @@ -136,6 +141,11 @@ class AutoCommissioner : public CommissioningDelegate static constexpr size_t kMaxDefaultNtpSize = 128; char mDefaultNtp[kMaxDefaultNtpSize]; + uint8_t mICDSymmetricKey[Crypto::kAES_CCM128_Key_Length]; + Platform::ScopedMemoryBufferWithSize mExtraReadPaths; + + // END memory space for commisisoning parameters + bool mNeedsNetworkSetup = false; ReadCommissioningInfo mDeviceCommissioningInfo; bool mNeedsDST = false; @@ -155,8 +165,6 @@ class AutoCommissioner : public CommissioningDelegate uint8_t mAttestationElements[Credentials::kMaxRspLen]; uint16_t mAttestationSignatureLen = 0; uint8_t mAttestationSignature[Crypto::kMax_ECDSA_Signature_Length]; - - uint8_t mICDSymmetricKey[Crypto::kAES_CCM128_Key_Length]; }; } // namespace Controller } // namespace chip diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 76e82d7f21ac85..e62bfb86916345 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -2329,6 +2329,12 @@ void DeviceCommissioner::ContinueReadingCommissioningInfo(const CommissioningPar Clusters::IcdManagement::Attributes::ActiveModeDuration::Id)); VerifyOrReturn(builder.AddAttributePath(kRootEndpointId, Clusters::IcdManagement::Id, Clusters::IcdManagement::Attributes::ActiveModeThreshold::Id)); + + // Extra paths requested via CommissioningParameters + for (auto const & path : params.GetExtraReadPaths()) + { + VerifyOrReturn(builder.AddAttributePath(path)); + } }(); VerifyOrDie(builder.size() > 0); // our logic is broken if there is nothing to read @@ -2363,6 +2369,7 @@ void DeviceCommissioner::FinishReadingCommissioningInfo() // up returning an error (e.g. because some mandatory information was missing). CHIP_ERROR err = CHIP_NO_ERROR; ReadCommissioningInfo info; + info.attributes = mAttributeCache.get(); AccumulateErrors(err, ParseGeneralCommissioningInfo(info)); AccumulateErrors(err, ParseBasicInformation(info)); AccumulateErrors(err, ParseNetworkCommissioningInfo(info)); diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index 93a0a58752900a..c9f1652c969be8 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -816,6 +816,7 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, Optional GetCommissioningParameters() { + // TODO: Return a non-optional const & to avoid a copy, mDefaultCommissioner is never null return mDefaultCommissioner == nullptr ? NullOptional : MakeOptional(mDefaultCommissioner->GetCommissioningParameters()); } diff --git a/src/controller/CommissioningDelegate.h b/src/controller/CommissioningDelegate.h index 660a3d11d128f9..99763aa305cabd 100644 --- a/src/controller/CommissioningDelegate.h +++ b/src/controller/CommissioningDelegate.h @@ -18,11 +18,14 @@ #pragma once #include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -591,6 +594,18 @@ class CommissioningParameters } void ClearICDStayActiveDurationMsec() { mICDStayActiveDurationMsec.ClearValue(); } + Span GetExtraReadPaths() const { return mExtraReadPaths; } + + // Additional attribute paths to read as part of the kReadCommissioningInfo stage. + // These values read from the device will be available in ReadCommissioningInfo.attributes. + // Clients should avoid requesting paths that are already read internally by the commissioner + // as no consolidation of internally read and extra paths provided here will be performed. + CommissioningParameters & SetExtraReadPaths(Span paths) + { + mExtraReadPaths = paths; + return *this; + } + // Clear all members that depend on some sort of external buffer. Can be // used to make sure that we are not holding any dangling pointers. void ClearExternalBufferDependentValues() @@ -613,6 +628,7 @@ class CommissioningParameters mDSTOffsets.ClearValue(); mDefaultNTP.ClearValue(); mICDSymmetricKey.ClearValue(); + mExtraReadPaths = decltype(mExtraReadPaths)(); } private: @@ -662,6 +678,7 @@ class CommissioningParameters Optional mICDStayActiveDurationMsec; ICDRegistrationStrategy mICDRegistrationStrategy = ICDRegistrationStrategy::kIgnore; bool mCheckForMatchingFabric = false; + Span mExtraReadPaths; }; struct RequestedCertificate @@ -762,6 +779,9 @@ struct ICDManagementClusterInfo struct ReadCommissioningInfo { +#if CHIP_CONFIG_ENABLE_READ_CLIENT + app::ClusterStateCache const * attributes = nullptr; +#endif NetworkClusters network; BasicClusterInfo basic; GeneralCommissioningInfo general; diff --git a/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.h b/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.h index a9adc87bb76f2b..22d50cf515578e 100644 --- a/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.h +++ b/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.h @@ -1,6 +1,5 @@ -/* - * - * Copyright (c) 2021 Project CHIP Authors +/** + * Copyright (c) 2021-2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,17 +15,21 @@ * limitations under the License. */ -#pragma once - #import +#include #include #include #include NS_ASSUME_NONNULL_BEGIN +// Decodes an attribute value TLV into a typed ObjC value (see MTRStructsObjc.h) id _Nullable MTRDecodeAttributeValue(const chip::app::ConcreteAttributePath & aPath, chip::TLV::TLVReader & aReader, CHIP_ERROR * aError); +// Wrapper around the precending function that reads the attribute from a ClusterStateCache. +id _Nullable MTRDecodeAttributeValue(const chip::app::ConcreteAttributePath & aPath, const chip::app::ClusterStateCache & aCache, + CHIP_ERROR * aError); + NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.mm b/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.mm new file mode 100644 index 00000000000000..d92da2f088aaa5 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRAttributeTLVValueDecoder_Internal.mm @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "MTRAttributeTLVValueDecoder_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +using namespace chip; + +id _Nullable MTRDecodeAttributeValue(const chip::app::ConcreteAttributePath & aPath, + const chip::app::ClusterStateCache & aCache, + CHIP_ERROR * aError) +{ + TLV::TLVReader reader; + *aError = aCache.Get(aPath, reader); + VerifyOrReturnValue(*aError == CHIP_NO_ERROR, nil); + return MTRDecodeAttributeValue(aPath, reader, aError); +} + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRCommissioningParameters.h b/src/darwin/Framework/CHIP/MTRCommissioningParameters.h index 771dfe54fc8893..3dcd1efc53dcac 100644 --- a/src/darwin/Framework/CHIP/MTRCommissioningParameters.h +++ b/src/darwin/Framework/CHIP/MTRCommissioningParameters.h @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2022-2023 Project CHIP Authors + * Copyright (c) 2022-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,6 +96,12 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) */ @property (nonatomic, copy, nullable) NSString * countryCode MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)); +/** + * Read device type information from all endpoints during commissioning. + * Defaults to NO. + */ +@property (nonatomic, assign) BOOL readEndpointInformation MTR_NEWLY_AVAILABLE; + @end @interface MTRCommissioningParameters (Deprecated) diff --git a/src/darwin/Framework/CHIP/MTRDefines_Internal.h b/src/darwin/Framework/CHIP/MTRDefines_Internal.h index ba7d6be51d61f5..a93be2e6cf6f79 100644 --- a/src/darwin/Framework/CHIP/MTRDefines_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDefines_Internal.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,8 +30,12 @@ #ifdef DEBUG #define MTR_TESTABLE MTR_EXPORT +#define MTR_TESTABLE_DIRECT +#define MTR_TESTABLE_DIRECT_MEMBERS #else #define MTR_TESTABLE +#define MTR_TESTABLE_DIRECT MTR_DIRECT +#define MTR_TESTABLE_DIRECT_MEMBERS MTR_DIRECT_MEMBERS #endif // clang-format off diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index 3b222a1e3eccfc..2380a5af846ff1 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2020-2023 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -656,11 +655,13 @@ - (void)controller:(MTRDeviceController *)controller } logString:__PRETTY_FUNCTION__]; } -- (void)controller:(MTRDeviceController *)controller readCommissioningInfo:(MTRProductIdentity *)info +- (void)controller:(MTRDeviceController *)controller readCommissioneeInfo:(MTRCommissioneeInfo *)info { [self _callDelegatesWithBlock:^(id delegate) { - if ([delegate respondsToSelector:@selector(controller:readCommissioningInfo:)]) { - [delegate controller:controller readCommissioningInfo:info]; + if ([delegate respondsToSelector:@selector(controller:readCommissioneeInfo:)]) { + [delegate controller:controller readCommissioneeInfo:info]; + } else if ([delegate respondsToSelector:@selector(controller:readCommissioningInfo:)]) { + [delegate controller:controller readCommissioningInfo:info.productIdentity]; } } logString:__PRETTY_FUNCTION__]; } diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegate.h b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegate.h index bd575e5a42def3..6a52919edd6cb8 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegate.h +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegate.h @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2020-2023 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +19,12 @@ NS_ASSUME_NONNULL_BEGIN +@class MTRDeviceController; +@class MTRDeviceTypeRevision; +@class MTREndpointInfo; +@class MTRMetrics; +@class MTRProductIdentity; + typedef NS_ENUM(NSInteger, MTRCommissioningStatus) { MTRCommissioningStatusUnknown = 0, MTRCommissioningStatusSuccess = 1, @@ -30,20 +35,31 @@ typedef NS_ENUM(NSInteger, MTRCommissioningStatus) { } MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); /** - * A representation of a (vendor, product) pair that identifies a specific product. + * Information read from the commissionee device during commissioning. */ -MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)) -@interface MTRProductIdentity : NSObject +MTR_NEWLY_AVAILABLE +@interface MTRCommissioneeInfo : NSObject -@property (nonatomic, copy, readonly) NSNumber * vendorID; +/** + * The product identity (VID / PID) of the commissionee. + */ +@property (nonatomic, copy, readonly) MTRProductIdentity * productIdentity; -@property (nonatomic, copy, readonly) NSNumber * productID; +/** + * Endpoint information for all endpoints of the commissionee. + * Will be present only if readEndpointInformation is set to YES on MTRCommissioningParameters. + * + * Use `rootEndpoint` and `-[MTREndpointInfo children]` to traverse endpoints in composition order. + */ +@property (nonatomic, copy, readonly, nullable) NSDictionary * endpointsById; -- (instancetype)initWithVendorID:(NSNumber *)vendorID productID:(NSNumber *)productID; -@end +/** + * Endpoint information for the root endpoint of the commissionee. + * Will be present only if readEndpointInformation is set to YES on MTRCommissioningParameters. + */ +@property (nonatomic, copy, readonly, nullable) MTREndpointInfo * rootEndpoint; -@class MTRDeviceController; -@class MTRMetrics; +@end /** * The protocol definition for the MTRDeviceControllerDelegate. @@ -98,14 +114,18 @@ MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) metrics:(MTRMetrics *)metrics MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)); /** - * Notify the delegate when commissioning infomation has been read from the Basic - * Information cluster of the commissionee. + * Notify the delegate when commissioning infomation has been read from the commissionee. * - * At the point when this notification happens, device attestation has not been performed yet, + * Note that this notification happens before device attestation is performed, * so the information delivered by this notification should not be trusted. */ - (void)controller:(MTRDeviceController *)controller - readCommissioningInfo:(MTRProductIdentity *)info MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)); + readCommissioneeInfo:(MTRCommissioneeInfo *)info MTR_NEWLY_AVAILABLE; + +- (void)controller:(MTRDeviceController *)controller + readCommissioningInfo:(MTRProductIdentity *)info + MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)) + MTR_NEWLY_DEPRECATED("Use controller:readCommissioneeInfo:"); /** * Notify the delegate when the suspended state changed of the controller, after this happens diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm index f19306eb7b67d0..664f2eee1ad024 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerDelegateBridge.mm @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,13 +17,39 @@ #import "MTRDeviceControllerDelegateBridge.h" #import "MTRDeviceController.h" #import "MTRDeviceController_Internal.h" +#import "MTREndpointInfo_Internal.h" #import "MTRError_Internal.h" #import "MTRLogging_Internal.h" #import "MTRMetricKeys.h" #import "MTRMetricsCollector.h" +#import "MTRProductIdentity.h" using namespace chip::Tracing::DarwinFramework; +@implementation MTRCommissioneeInfo + +- (instancetype)initWithCommissioningInfo:(const chip::Controller::ReadCommissioningInfo &)info +{ + self = [super init]; + _productIdentity = [[MTRProductIdentity alloc] initWithVendorID:@(info.basic.vendorId) productID:@(info.basic.productId)]; + + // TODO: We should probably hold onto our MTRCommissioningParameters so we can look at `readEndpointInformation` + // instead of just reading whatever Descriptor cluster information happens to be in the cache. + auto * endpoints = [MTREndpointInfo endpointsFromAttributeCache:info.attributes]; + if (endpoints.count > 0) { + _endpointsById = endpoints; + } + + return self; +} + +- (MTREndpointInfo *)rootEndpoint +{ + return self.endpointsById[@0]; +} + +@end + MTRDeviceControllerDelegateBridge::MTRDeviceControllerDelegateBridge(void) : mDelegate(nil) { @@ -124,20 +149,21 @@ void MTRDeviceControllerDelegateBridge::OnReadCommissioningInfo(const chip::Controller::ReadCommissioningInfo & info) { MTRDeviceController * strongController = mController; - - chip::VendorId vendorId = info.basic.vendorId; - uint16_t productId = info.basic.productId; - - MTR_LOG("%@ DeviceControllerDelegate Read Commissioning Info. VendorId %u ProductId %u", strongController, vendorId, productId); - id strongDelegate = mDelegate; - if (strongDelegate && mQueue && strongController) { - if ([strongDelegate respondsToSelector:@selector(controller:readCommissioningInfo:)]) { - dispatch_async(mQueue, ^{ - auto * info = [[MTRProductIdentity alloc] initWithVendorID:@(vendorId) productID:@(productId)]; - [strongDelegate controller:strongController readCommissioningInfo:info]; - }); - } + VerifyOrReturn(strongDelegate && mQueue && strongController); + + // TODO: These checks are pointless since currently mController == mDelegate + BOOL wantCommissioneeInfo = [strongDelegate respondsToSelector:@selector(controller:readCommissioneeInfo:)]; + BOOL wantProductIdentity = [strongDelegate respondsToSelector:@selector(controller:readCommissioningInfo:)]; + if (wantCommissioneeInfo || wantProductIdentity) { + auto * commissioneeInfo = [[MTRCommissioneeInfo alloc] initWithCommissioningInfo:info]; + dispatch_async(mQueue, ^{ + if (wantCommissioneeInfo) { // prefer the newer delegate method over the deprecated one + [strongDelegate controller:strongController readCommissioneeInfo:commissioneeInfo]; + } else if (wantProductIdentity) { + [strongDelegate controller:strongController readCommissioningInfo:commissioneeInfo.productIdentity]; + } + }); } } @@ -187,16 +213,3 @@ { mDeviceNodeId = deviceNodeId; } - -@implementation MTRProductIdentity - -- (instancetype)initWithVendorID:(NSNumber *)vendorID productID:(NSNumber *)productID -{ - if (self = [super init]) { - _vendorID = vendorID; - _productID = productID; - } - return self; -} - -@end diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_Concrete.mm b/src/darwin/Framework/CHIP/MTRDeviceController_Concrete.mm index b27844425baa50..b67b07e8b81ba3 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_Concrete.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController_Concrete.mm @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2020-2023 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +33,7 @@ #import "MTRDeviceController_Concrete.h" #import "MTRDevice_Concrete.h" #import "MTRDevice_Internal.h" +#import "MTREndpointInfo_Internal.h" #import "MTRError_Internal.h" #import "MTRKeypair.h" #import "MTRLogging_Internal.h" @@ -961,6 +961,9 @@ - (BOOL)commissionNodeWithID:(NSNumber *)nodeID auto block = ^BOOL { chip::Controller::CommissioningParameters params; + if (commissioningParams.readEndpointInformation) { + params.SetExtraReadPaths(MTREndpointInfo.requiredAttributePaths); + } if (commissioningParams.csrNonce) { params.SetCSRNonce(AsByteSpan(commissioningParams.csrNonce)); } diff --git a/src/darwin/Framework/CHIP/MTRDeviceType.h b/src/darwin/Framework/CHIP/MTRDeviceType.h index 0330b76d1533a2..54802a19e12dd6 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceType.h +++ b/src/darwin/Framework/CHIP/MTRDeviceType.h @@ -23,8 +23,11 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Meta-data about a device type defined in the Matter specification. + */ MTR_AVAILABLE(ios(18.2), macos(15.2), watchos(11.2), tvos(18.2)) -@interface MTRDeviceType : NSObject +@interface MTRDeviceType : NSObject /* (see below) */ /** * Returns an MTRDeviceType for the given ID, if the ID is known. Returns nil @@ -52,4 +55,8 @@ MTR_AVAILABLE(ios(18.2), macos(15.2), watchos(11.2), tvos(18.2)) @end +MTR_NEWLY_AVAILABLE +@interface MTRDeviceType () +@end + NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRDeviceType.mm b/src/darwin/Framework/CHIP/MTRDeviceType.mm index 394640d2d72b2a..4cea1d33b9c8dc 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceType.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceType.mm @@ -17,27 +17,42 @@ #import +#import "MTRDefines_Internal.h" #import "MTRDeviceTypeMetadata.h" #import "MTRLogging_Internal.h" +#include #include using namespace chip; -@implementation MTRDeviceType +MTR_DIRECT_MEMBERS +@implementation MTRDeviceType { + const MTRDeviceTypeData * _meta; +} -- (nullable instancetype)initWithDeviceTypeID:(NSNumber *)id name:(NSString *)name isUtility:(BOOL)isUtility +- (instancetype)initWithDeviceTypeData:(const MTRDeviceTypeData *)metaData { - if (!(self = [super init])) { - return nil; - } - - _id = id; - _name = name; - _isUtility = isUtility; + self = [super init]; + _meta = metaData; return self; } +- (NSNumber *)id +{ + return @(_meta->id); +} + +- (NSString *)name +{ + return _meta->name; +} + +- (BOOL)isUtility +{ + return _meta->deviceClass != MTRDeviceTypeClass::Simple; +} + + (nullable MTRDeviceType *)deviceTypeForID:(NSNumber *)deviceTypeID { if (!CanCastTo(deviceTypeID.unsignedLongLongValue)) { @@ -50,10 +65,30 @@ + (nullable MTRDeviceType *)deviceTypeForID:(NSNumber *)deviceTypeID return nil; } - return [[MTRDeviceType alloc] - initWithDeviceTypeID:deviceTypeID - name:[NSString stringWithUTF8String:deviceTypeData->name] - isUtility:(deviceTypeData->deviceClass != MTRDeviceTypeClass::Simple)]; + return [[MTRDeviceType alloc] initWithDeviceTypeData:deviceTypeData]; +} + +- (id)copyWithZone:(NSZone *)zone +{ + return self; // immutable +} + +- (NSUInteger)hash +{ + return _meta->id; +} + +- (BOOL)isEqual:(id)object +{ + VerifyOrReturnValue([object class] == [self class], NO); + MTRDeviceType * other = object; + return _meta->id == other->_meta->id; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ 0x%" PRIx32 " (%@)>", + self.class, _meta->id, _meta->name]; } @end diff --git a/src/darwin/Framework/CHIP/MTRDeviceTypeMetadata.h b/src/darwin/Framework/CHIP/MTRDeviceTypeMetadata.h index 2597f60ec47415..b0f1a280532756 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceTypeMetadata.h +++ b/src/darwin/Framework/CHIP/MTRDeviceTypeMetadata.h @@ -23,18 +23,16 @@ NS_ASSUME_NONNULL_BEGIN -enum class MTRDeviceTypeClass -{ +enum class MTRDeviceTypeClass { Utility, Simple, Node, // Might not be a real class, but we have it for Root Node for now. }; -struct MTRDeviceTypeData -{ +struct MTRDeviceTypeData { chip::DeviceTypeId id; MTRDeviceTypeClass deviceClass; - const char * name; + NSString * name; }; // Returns null for unknown device types. diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.h b/src/darwin/Framework/CHIP/MTRDeviceTypeRevision.h similarity index 75% rename from src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.h rename to src/darwin/Framework/CHIP/MTRDeviceTypeRevision.h index 517948816364c2..55865ed952b719 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.h +++ b/src/darwin/Framework/CHIP/MTRDeviceTypeRevision.h @@ -17,6 +17,9 @@ #import #import +@class MTRDescriptorClusterDeviceTypeStruct; +@class MTRDeviceType; + NS_ASSUME_NONNULL_BEGIN /** @@ -38,9 +41,20 @@ MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) */ - (nullable instancetype)initWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision; +/** + * Initializes the receiver based on the values in the specified struct. + */ +- (nullable instancetype)initWithDeviceTypeStruct:(MTRDescriptorClusterDeviceTypeStruct *)deviceTypeStruct MTR_NEWLY_AVAILABLE; + @property (nonatomic, copy, readonly) NSNumber * deviceTypeID; @property (nonatomic, copy, readonly) NSNumber * deviceTypeRevision; +/** + * Returns the MTRDeviceType corresponding to deviceTypeID, + * or nil if deviceTypeID does not represent a known device type. + */ +@property (nonatomic, copy, readonly, nullable) MTRDeviceType * typeInformation MTR_NEWLY_AVAILABLE; + @end NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.mm b/src/darwin/Framework/CHIP/MTRDeviceTypeRevision.mm similarity index 76% rename from src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.mm rename to src/darwin/Framework/CHIP/MTRDeviceTypeRevision.mm index de5782c92e4aeb..e637f83543f83b 100644 --- a/src/darwin/Framework/CHIP/ServerEndpoint/MTRDeviceTypeRevision.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceTypeRevision.mm @@ -14,12 +14,15 @@ * limitations under the License. */ +#import "MTRDeviceTypeRevision.h" #import "MTRDefines_Internal.h" +#import "MTRDeviceType.h" #import "MTRLogging_Internal.h" -#import +#import "MTRStructsObjc.h" #include #include +#include #include using namespace chip; @@ -29,6 +32,9 @@ @implementation MTRDeviceTypeRevision - (nullable instancetype)initWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision { + VerifyOrReturnValue(deviceTypeID != nil, nil); + VerifyOrReturnValue(revision != nil, nil); + auto deviceTypeIDValue = deviceTypeID.unsignedLongLongValue; if (!CanCastTo(deviceTypeIDValue)) { MTR_LOG_ERROR("MTRDeviceTypeRevision provided too-large device type ID: 0x%llx", deviceTypeIDValue); @@ -50,19 +56,26 @@ - (nullable instancetype)initWithDeviceTypeID:(NSNumber *)deviceTypeID revision: return [self initInternalWithDeviceTypeID:[deviceTypeID copy] revision:[revision copy]]; } +- (instancetype)initWithDeviceTypeStruct:(MTRDescriptorClusterDeviceTypeStruct *)deviceTypeStruct +{ + return [self initWithDeviceTypeID:deviceTypeStruct.deviceType revision:deviceTypeStruct.revision]; +} + // initInternalWithDeviceTypeID:revision assumes that the device type ID and device // revision have already been validated and, if needed, copied from the input. - (instancetype)initInternalWithDeviceTypeID:(NSNumber *)deviceTypeID revision:(NSNumber *)revision { - if (!(self = [super init])) { - return nil; - } - + self = [super init]; _deviceTypeID = deviceTypeID; _deviceTypeRevision = revision; return self; } +- (MTRDeviceType *)typeInformation +{ + return [MTRDeviceType deviceTypeForID:_deviceTypeID]; +} + - (id)copyWithZone:(NSZone *)zone { // We have no mutable state. @@ -85,4 +98,11 @@ - (NSUInteger)hash return _deviceTypeID.unsignedLongValue ^ _deviceTypeRevision.unsignedShortValue; } +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ 0x%" PRIx32 " (%@) rev %d>", + self.class, _deviceTypeID.unsignedIntValue, + self.typeInformation.name ?: @"???", _deviceTypeRevision.unsignedIntValue]; +} + @end diff --git a/src/darwin/Framework/CHIP/MTREndpointInfo.h b/src/darwin/Framework/CHIP/MTREndpointInfo.h new file mode 100644 index 00000000000000..d705e69767a80f --- /dev/null +++ b/src/darwin/Framework/CHIP/MTREndpointInfo.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@class MTRDeviceTypeRevision; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Meta-data about an endpoint of a Matter node. + */ +MTR_NEWLY_AVAILABLE +@interface MTREndpointInfo : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@property (nonatomic, copy, readonly) NSNumber * endpointID; + +@property (nonatomic, copy, readonly) NSArray * deviceTypes; +@property (nonatomic, copy, readonly) NSArray * partsList; + +/** + * The direct children of this endpoint. This excludes indirect descendants + * even if they are listed in the PartsList attribute of this endpoint due + * to the Full-Family Pattern being used. Refer to Endpoint Composition Patterns + * in the Matter specification for details. + */ +@property (nonatomic, copy, readonly) NSArray * children; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTREndpointInfo.mm b/src/darwin/Framework/CHIP/MTREndpointInfo.mm new file mode 100644 index 00000000000000..c92b525ed99234 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTREndpointInfo.mm @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "MTREndpointInfo_Internal.h" + +#import "MTRAttributeTLVValueDecoder_Internal.h" +#import "MTRDeviceTypeRevision.h" +#import "MTRLogging_Internal.h" +#import "MTRStructsObjc.h" + +#include +#include + +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; + +enum class EndpointMark : uint8_t { + NotVisited = 0, + Visiting, + Visited, + ParentAssigned = NotVisited, // != Visited +}; + +MTR_DIRECT_MEMBERS +@implementation MTREndpointInfo { + EndpointId _endpointID; + EndpointMark _mark; // used by populateChildrenForEndpoints: +} + +- (instancetype)initWithEndpointID:(NSNumber *)endpointID + deviceTypes:(NSArray *)deviceTypes + partsList:(NSArray *)partsList +{ + self = [super init]; + _endpointID = endpointID.unsignedShortValue; + _deviceTypes = [deviceTypes copy]; + _partsList = [partsList copy]; + _children = @[]; + _mark = EndpointMark::NotVisited; + return self; +} + +- (NSNumber *)endpointID +{ + return @(_endpointID); +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ %u>", self.class, _endpointID]; +} + ++ (BOOL)populateChildrenForEndpoints:(NSDictionary *)endpoints +{ + // Populate the child list of each endpoint, ensuring no cycles (these are disallowed + // by the spec, but we need to ensure we don't create a retain cycle even under invalid + // input). Conservatively assume all endpoints use the Full-Family Pattern. + // Refer to "Endpoint Composition" in the Matter specification for details. + MTREndpointInfo * root = endpoints[@0]; + if (root == nil) { + MTR_LOG_ERROR("Missing root endpoint, not populating endpoint hierarchy"); + return NO; + } + + // Perform a depth-first search with an explicit stack and create a list of endpoint + // IDs in reverse topological order. Note that endpoints start with _mark == NotVisited. + BOOL valid = YES; + std::deque deque; // stack followed by sorted list + deque.emplace_front(root->_endpointID); + for (;;) { + EndpointId endpointID = deque.front(); + MTREndpointInfo * endpoint = endpoints[@(endpointID)]; + if (endpoint->_mark == EndpointMark::NotVisited) { + endpoint->_mark = EndpointMark::Visiting; + for (NSNumber * partNumber in endpoint->_partsList) { + MTREndpointInfo * descendant = endpoints[partNumber]; + if (!descendant) { + MTR_LOG_ERROR("Warning: PartsList of endpoint %u references non-existant endpoint %u", + endpointID, partNumber.unsignedShortValue); + valid = NO; + } else if (descendant->_mark == EndpointMark::NotVisited) { + deque.emplace_front(descendant->_endpointID); + } else if (descendant->_mark == EndpointMark::Visiting) { + MTR_LOG_ERROR("Warning: Cyclic endpoint composition involving endpoints %u and %u", + descendant->_endpointID, endpointID); + valid = NO; + } + } + } else if (endpoint->_mark == EndpointMark::Visiting) { + endpoint->_mark = EndpointMark::Visited; + deque.pop_front(); // remove from stack + deque.emplace_back(endpointID); // add to sorted list + if (endpointID == root->_endpointID) { + break; // visited the root, DFS traversal done + } + } else /* endpoint->_mark == EndpointMark::Visited */ { + // Nodes can be visited multiple times due to Full-Family + // ancestors like the root node, or in scenarios where a + // nodes is erroneously in the PartsList of two separate + // branches of the tree. There is no easy way to distinguish + // these cases here, so we are not setting valid = NO. + deque.pop_front(); // nothing else to do + } + } + if (deque.size() != endpoints.count) { + MTR_LOG_ERROR("Warning: Not all endpoints are descendants of the root endpoint"); + valid = NO; + } + + // Now iterate over the endpoints in reverse topological order, i.e. bottom up. This means + // that we will visit children before parents, so the first time we see an endpoint in a + // PartsList we can assign it as a child of the endpoint we're processing, and we can be sure + // that this is the closest parent, not some higher ancestor using the Full-Family Pattern. + NSMutableArray * children = [[NSMutableArray alloc] init]; + while (!deque.empty()) { + EndpointId endpointID = deque.front(); + MTREndpointInfo * endpoint = endpoints[@(endpointID)]; + deque.pop_front(); + + if (endpoint->_mark == EndpointMark::ParentAssigned) { + continue; // This endpoint is part of a cycle, don't populate its children. + } + + [children removeAllObjects]; + for (NSNumber * partNumber in endpoint->_partsList) { + MTREndpointInfo * descendant = endpoints[partNumber]; + if (descendant != nil && descendant->_mark != EndpointMark::ParentAssigned) { + descendant->_mark = EndpointMark::ParentAssigned; + [children addObject:descendant]; + } + } + endpoint->_children = [children copy]; + } + root->_mark = EndpointMark::ParentAssigned; + return valid; +} + ++ (NSDictionary *)endpointsFromAttributeCache:(const ClusterStateCache *)cache +{ + VerifyOrReturnValue(cache != nullptr, nil); + using namespace Descriptor::Attributes; + + NSMutableDictionary * endpoints = [[NSMutableDictionary alloc] init]; + cache->ForEachAttribute(Descriptor::Id, [&](const ConcreteAttributePath & path) -> CHIP_ERROR { + VerifyOrReturnError(path.mAttributeId == DeviceTypeList::Id, CHIP_NO_ERROR); + + CHIP_ERROR err = CHIP_NO_ERROR; + NSArray * deviceTypeList = MTRDecodeAttributeValue(path, *cache, &err); + if (!deviceTypeList) { + MTR_LOG_ERROR("Ignoring invalid DeviceTypeList for endpoint %u: %" CHIP_ERROR_FORMAT, path.mEndpointId, err.Format()); + // proceed with deviceTypeList == nil, equivalent to an empty list + } + + NSMutableArray * deviceTypes = [[NSMutableArray alloc] initWithCapacity:deviceTypeList.count]; + for (MTRDescriptorClusterDeviceTypeStruct * deviceTypeStruct in deviceTypeList) { + MTRDeviceTypeRevision * type = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeStruct:deviceTypeStruct]; + if (!type) { + MTR_LOG_ERROR("Ignoring invalid device type 0x%x rev %u for endpoint %u", + deviceTypeStruct.deviceType.unsignedIntValue, deviceTypeStruct.revision.unsignedShortValue, + path.mEndpointId); + continue; + } + [deviceTypes addObject:type]; + } + + ConcreteAttributePath partsListPath(path.mEndpointId, path.mClusterId, PartsList::Id); + NSArray * partsList = MTRDecodeAttributeValue(partsListPath, *cache, &err); + if (!partsList) { + MTR_LOG_ERROR("Ignoring invalid PartsList for endpoint %u: %" CHIP_ERROR_FORMAT, path.mEndpointId, err.Format()); + partsList = @[]; + } + + MTREndpointInfo * endpoint = [[MTREndpointInfo alloc] initWithEndpointID:@(path.mEndpointId) + deviceTypes:deviceTypes + partsList:partsList]; + endpoints[endpoint.endpointID] = endpoint; + return CHIP_NO_ERROR; + }); + + if (endpoints.count > 0) { + [self populateChildrenForEndpoints:endpoints]; + } + return [endpoints copy]; +} + ++ (Span)requiredAttributePaths +{ + using namespace Descriptor::Attributes; + static constexpr AttributePathParams kPaths[] = { + AttributePathParams(Descriptor::Id, DeviceTypeList::Id), + AttributePathParams(Descriptor::Id, PartsList::Id), + }; + return Span(kPaths); +} + +@end diff --git a/src/darwin/Framework/CHIP/MTREndpointInfo_Internal.h b/src/darwin/Framework/CHIP/MTREndpointInfo_Internal.h new file mode 100644 index 00000000000000..298ee8dab5241b --- /dev/null +++ b/src/darwin/Framework/CHIP/MTREndpointInfo_Internal.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "MTREndpointInfo_Test.h" + +#include +#include +#include + +NS_ASSUME_NONNULL_BEGIN + +MTR_DIRECT_MEMBERS +@interface MTREndpointInfo () + +/** + * Returns a dictionary of endpoint metadata for a node. + * + * The provided cache must contain the result of reading the + * DeviceTypeList and PartsList attributes of all endpoints + * (as exposed by `requiredAttributePaths`). + * + * Any relevant information will be copied out of the cache; + * the caller is free to deallocate the cache once this method returns. + */ ++ (NSDictionary *)endpointsFromAttributeCache:(const chip::app::ClusterStateCache *)cache; + +/** + * Returns the set of AttributePathParams that must be read + * to populate endpoint information for a node. + */ ++ (chip::Span)requiredAttributePaths; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTREndpointInfo_Test.h b/src/darwin/Framework/CHIP/MTREndpointInfo_Test.h new file mode 100644 index 00000000000000..543132d2e457fd --- /dev/null +++ b/src/darwin/Framework/CHIP/MTREndpointInfo_Test.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#import "MTRDefines_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +MTR_TESTABLE_DIRECT_MEMBERS +@interface MTREndpointInfo () + +- (instancetype)initWithEndpointID:(NSNumber *)endpointID + deviceTypes:(NSArray *)deviceTypes + partsList:(NSArray *)partsList NS_DESIGNATED_INITIALIZER; + +// Populates the children array for each endpoint in the provided dictionary. +// Returns YES if the endpoint hierarchy was populated correctly. +// A return value of NO indicates that there were some issues, but +// an effort has been made to populate a valid subset of the hierarchy. ++ (BOOL)populateChildrenForEndpoints:(NSDictionary *)endpoints; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h b/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h index 928c80ba85a0cb..86a054a550c504 100644 --- a/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h +++ b/src/darwin/Framework/CHIP/MTROperationalCredentialsDelegate.h @@ -1,6 +1,5 @@ /** - * - * Copyright (c) 2021-2023 Project CHIP Authors + * Copyright (c) 2021-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +14,6 @@ * limitations under the License. */ -#include - #import #import @@ -32,6 +29,8 @@ #include #include +#include + NS_ASSUME_NONNULL_BEGIN class MTROperationalCredentialsDelegate : public chip::Controller::OperationalCredentialsDelegate { @@ -63,11 +62,6 @@ class MTROperationalCredentialsDelegate : public chip::Controller::OperationalCr mCppCommissioner = cppCommissioner; } - chip::Optional GetCommissioningParameters() - { - return mCppCommissioner == nullptr ? chip::NullOptional : mCppCommissioner->GetCommissioningParameters(); - } - void SetOperationalCertificateIssuer( id operationalCertificateIssuer, dispatch_queue_t operationalCertificateIssuerQueue) { diff --git a/src/darwin/Framework/CHIP/MTRProductIdentity.h b/src/darwin/Framework/CHIP/MTRProductIdentity.h new file mode 100644 index 00000000000000..88183188369d91 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRProductIdentity.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A representation of a (vendor, product) pair that identifies a specific product. + */ +MTR_AVAILABLE(ios(17.0), macos(14.0), watchos(10.0), tvos(17.0)) +@interface MTRProductIdentity : NSObject /* (see below) */ + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +- (instancetype)initWithVendorID:(NSNumber *)vendorID productID:(NSNumber *)productID; + +@property (nonatomic, copy, readonly) NSNumber * vendorID; +@property (nonatomic, copy, readonly) NSNumber * productID; + +@end + +MTR_NEWLY_AVAILABLE +@interface MTRProductIdentity () +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRProductIdentity.mm b/src/darwin/Framework/CHIP/MTRProductIdentity.mm new file mode 100644 index 00000000000000..2f03600e3e7487 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRProductIdentity.mm @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "MTRProductIdentity.h" + +#import "MTRDefines_Internal.h" +#import "MTRUtilities.h" + +#include + +MTR_DIRECT_MEMBERS +@implementation MTRProductIdentity + +- (instancetype)initWithVendorID:(NSNumber *)vendorID productID:(NSNumber *)productID +{ + self = [super init]; + VerifyOrReturnValue(vendorID != nil && productID != nil, nil); + _vendorID = vendorID; + _productID = productID; + return self; +} + +- (id)copyWithZone:(NSZone *)zone +{ + return self; // immutable +} + +- (NSUInteger)hash +{ + return _vendorID.hash ^ _productID.hash; +} + +- (BOOL)isEqual:(id)object +{ + VerifyOrReturnValue([object class] == [self class], NO); + MTRProductIdentity * other = object; + return MTREqualObjects(_vendorID, other.vendorID) && MTREqualObjects(_productID, other.productID); +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: vid 0x%x pid 0x%x>", self.class, _vendorID.unsignedIntValue, _productID.unsignedIntValue]; +} + +@end diff --git a/src/darwin/Framework/CHIP/Matter.h b/src/darwin/Framework/CHIP/Matter.h index 85a87c4ba12c0c..55a4b31e816e63 100644 --- a/src/darwin/Framework/CHIP/Matter.h +++ b/src/darwin/Framework/CHIP/Matter.h @@ -52,6 +52,7 @@ #import #import #import +#import #import #import #import @@ -62,6 +63,7 @@ #import #import #import +#import #import #import #import diff --git a/src/darwin/Framework/CHIP/templates/MTRDeviceTypeMetadata-src.zapt b/src/darwin/Framework/CHIP/templates/MTRDeviceTypeMetadata-src.zapt index 16322f398e9694..69f064ac746ff8 100644 --- a/src/darwin/Framework/CHIP/templates/MTRDeviceTypeMetadata-src.zapt +++ b/src/darwin/Framework/CHIP/templates/MTRDeviceTypeMetadata-src.zapt @@ -9,7 +9,7 @@ namespace { constexpr MTRDeviceTypeData knownDeviceTypes[] = { {{#zcl_device_types}} {{#if class}} - { {{asHex code 8}}, MTRDeviceTypeClass::{{class}}, "{{caption}}" }, + { {{asHex code 8}}, MTRDeviceTypeClass::{{class}}, @"{{caption}}" }, {{/if}} {{/zcl_device_types}} }; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRDeviceTypeMetadata.mm b/src/darwin/Framework/CHIP/zap-generated/MTRDeviceTypeMetadata.mm index cec53390010b33..9e4b98f3ffdb77 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRDeviceTypeMetadata.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRDeviceTypeMetadata.mm @@ -21,77 +21,79 @@ namespace { -constexpr MTRDeviceTypeData knownDeviceTypes[] = { - { 0x0000000A, MTRDeviceTypeClass::Simple, "Door Lock" }, - { 0x0000000B, MTRDeviceTypeClass::Simple, "Door Lock Controller" }, - { 0x0000000E, MTRDeviceTypeClass::Simple, "Aggregator" }, - { 0x0000000F, MTRDeviceTypeClass::Simple, "Generic Switch" }, - { 0x00000011, MTRDeviceTypeClass::Utility, "Power Source" }, - { 0x00000012, MTRDeviceTypeClass::Utility, "OTA Requestor" }, - { 0x00000013, MTRDeviceTypeClass::Utility, "Bridged Node" }, - { 0x00000014, MTRDeviceTypeClass::Utility, "OTA Provider" }, - { 0x00000015, MTRDeviceTypeClass::Simple, "Contact Sensor" }, - { 0x00000016, MTRDeviceTypeClass::Node, "Root Node" }, - { 0x00000017, MTRDeviceTypeClass::Simple, "Solar Power" }, - { 0x00000018, MTRDeviceTypeClass::Simple, "Battery Storage" }, - { 0x00000019, MTRDeviceTypeClass::Utility, "Secondary Network Interface" }, - { 0x00000022, MTRDeviceTypeClass::Simple, "Speaker" }, - { 0x00000023, MTRDeviceTypeClass::Simple, "Casting Video Player" }, - { 0x00000024, MTRDeviceTypeClass::Simple, "Content App" }, - { 0x00000027, MTRDeviceTypeClass::Simple, "Mode Select" }, - { 0x00000028, MTRDeviceTypeClass::Simple, "Basic Video Player" }, - { 0x00000029, MTRDeviceTypeClass::Simple, "Casting Video Client" }, - { 0x0000002A, MTRDeviceTypeClass::Simple, "Video Remote Control" }, - { 0x0000002B, MTRDeviceTypeClass::Simple, "Fan" }, - { 0x0000002C, MTRDeviceTypeClass::Simple, "Air Quality Sensor" }, - { 0x0000002D, MTRDeviceTypeClass::Simple, "Air Purifier" }, - { 0x00000041, MTRDeviceTypeClass::Simple, "Water Freeze Detector" }, - { 0x00000042, MTRDeviceTypeClass::Simple, "Water Valve" }, - { 0x00000043, MTRDeviceTypeClass::Simple, "Water Leak Detector" }, - { 0x00000044, MTRDeviceTypeClass::Simple, "Rain Sensor" }, - { 0x00000070, MTRDeviceTypeClass::Simple, "Refrigerator" }, - { 0x00000071, MTRDeviceTypeClass::Simple, "Temperature Controlled Cabinet" }, - { 0x00000072, MTRDeviceTypeClass::Simple, "Room Air Conditioner" }, - { 0x00000073, MTRDeviceTypeClass::Simple, "Laundry Washer" }, - { 0x00000074, MTRDeviceTypeClass::Simple, "Robotic Vacuum Cleaner" }, - { 0x00000075, MTRDeviceTypeClass::Simple, "Dishwasher" }, - { 0x00000076, MTRDeviceTypeClass::Simple, "Smoke CO Alarm" }, - { 0x00000077, MTRDeviceTypeClass::Simple, "Cook Surface" }, - { 0x00000078, MTRDeviceTypeClass::Simple, "Cooktop" }, - { 0x00000079, MTRDeviceTypeClass::Simple, "Microwave Oven" }, - { 0x0000007A, MTRDeviceTypeClass::Simple, "Extractor Hood" }, - { 0x0000007B, MTRDeviceTypeClass::Simple, "Oven" }, - { 0x0000007C, MTRDeviceTypeClass::Simple, "Laundry Dryer" }, - { 0x00000090, MTRDeviceTypeClass::Simple, "Network Infrastructure Manager" }, - { 0x00000091, MTRDeviceTypeClass::Simple, "Thread Border Router" }, - { 0x00000100, MTRDeviceTypeClass::Simple, "On/Off Light" }, - { 0x00000101, MTRDeviceTypeClass::Simple, "Dimmable Light" }, - { 0x00000103, MTRDeviceTypeClass::Simple, "On/Off Light Switch" }, - { 0x00000104, MTRDeviceTypeClass::Simple, "Dimmer Switch" }, - { 0x00000105, MTRDeviceTypeClass::Simple, "Color Dimmer Switch" }, - { 0x00000106, MTRDeviceTypeClass::Simple, "Light Sensor" }, - { 0x00000107, MTRDeviceTypeClass::Simple, "Occupancy Sensor" }, - { 0x0000010A, MTRDeviceTypeClass::Simple, "On/Off Plug-in Unit" }, - { 0x0000010B, MTRDeviceTypeClass::Simple, "Dimmable Plug-in Unit" }, - { 0x0000010C, MTRDeviceTypeClass::Simple, "Color Temperature Light" }, - { 0x0000010D, MTRDeviceTypeClass::Simple, "Extended Color Light" }, - { 0x00000202, MTRDeviceTypeClass::Simple, "Window Covering" }, - { 0x00000203, MTRDeviceTypeClass::Simple, "Window Covering Controller" }, - { 0x00000300, MTRDeviceTypeClass::Simple, "Heating/Cooling Unit" }, - { 0x00000301, MTRDeviceTypeClass::Simple, "Thermostat" }, - { 0x00000302, MTRDeviceTypeClass::Simple, "Temperature Sensor" }, - { 0x00000303, MTRDeviceTypeClass::Simple, "Pump" }, - { 0x00000304, MTRDeviceTypeClass::Simple, "Pump Controller" }, - { 0x00000305, MTRDeviceTypeClass::Simple, "Pressure Sensor" }, - { 0x00000306, MTRDeviceTypeClass::Simple, "Flow Sensor" }, - { 0x00000307, MTRDeviceTypeClass::Simple, "Humidity Sensor" }, - { 0x00000309, MTRDeviceTypeClass::Simple, "Heat Pump" }, - { 0x0000050C, MTRDeviceTypeClass::Simple, "EVSE" }, - { 0x0000050D, MTRDeviceTypeClass::Utility, "Device Energy Management" }, - { 0x0000050F, MTRDeviceTypeClass::Simple, "Water Heater" }, - { 0x00000510, MTRDeviceTypeClass::Utility, "Electrical Sensor" }, - { 0x00000840, MTRDeviceTypeClass::Simple, "Control Bridge" }, - { 0x00000850, MTRDeviceTypeClass::Simple, "On/Off Sensor" }, +// Not constexpr in the strict sense because NSString * is not a literal +// type, but the array is in fact constant initialized by the compiler. +static /* constexpr */ const MTRDeviceTypeData knownDeviceTypes[] = { + { 0x0000000A, MTRDeviceTypeClass::Simple, @"Door Lock" }, + { 0x0000000B, MTRDeviceTypeClass::Simple, @"Door Lock Controller" }, + { 0x0000000E, MTRDeviceTypeClass::Simple, @"Aggregator" }, + { 0x0000000F, MTRDeviceTypeClass::Simple, @"Generic Switch" }, + { 0x00000011, MTRDeviceTypeClass::Utility, @"Power Source" }, + { 0x00000012, MTRDeviceTypeClass::Utility, @"OTA Requestor" }, + { 0x00000013, MTRDeviceTypeClass::Utility, @"Bridged Node" }, + { 0x00000014, MTRDeviceTypeClass::Utility, @"OTA Provider" }, + { 0x00000015, MTRDeviceTypeClass::Simple, @"Contact Sensor" }, + { 0x00000016, MTRDeviceTypeClass::Node, @"Root Node" }, + { 0x00000017, MTRDeviceTypeClass::Simple, @"Solar Power" }, + { 0x00000018, MTRDeviceTypeClass::Simple, @"Battery Storage" }, + { 0x00000019, MTRDeviceTypeClass::Utility, @"Secondary Network Interface" }, + { 0x00000022, MTRDeviceTypeClass::Simple, @"Speaker" }, + { 0x00000023, MTRDeviceTypeClass::Simple, @"Casting Video Player" }, + { 0x00000024, MTRDeviceTypeClass::Simple, @"Content App" }, + { 0x00000027, MTRDeviceTypeClass::Simple, @"Mode Select" }, + { 0x00000028, MTRDeviceTypeClass::Simple, @"Basic Video Player" }, + { 0x00000029, MTRDeviceTypeClass::Simple, @"Casting Video Client" }, + { 0x0000002A, MTRDeviceTypeClass::Simple, @"Video Remote Control" }, + { 0x0000002B, MTRDeviceTypeClass::Simple, @"Fan" }, + { 0x0000002C, MTRDeviceTypeClass::Simple, @"Air Quality Sensor" }, + { 0x0000002D, MTRDeviceTypeClass::Simple, @"Air Purifier" }, + { 0x00000041, MTRDeviceTypeClass::Simple, @"Water Freeze Detector" }, + { 0x00000042, MTRDeviceTypeClass::Simple, @"Water Valve" }, + { 0x00000043, MTRDeviceTypeClass::Simple, @"Water Leak Detector" }, + { 0x00000044, MTRDeviceTypeClass::Simple, @"Rain Sensor" }, + { 0x00000070, MTRDeviceTypeClass::Simple, @"Refrigerator" }, + { 0x00000071, MTRDeviceTypeClass::Simple, @"Temperature Controlled Cabinet" }, + { 0x00000072, MTRDeviceTypeClass::Simple, @"Room Air Conditioner" }, + { 0x00000073, MTRDeviceTypeClass::Simple, @"Laundry Washer" }, + { 0x00000074, MTRDeviceTypeClass::Simple, @"Robotic Vacuum Cleaner" }, + { 0x00000075, MTRDeviceTypeClass::Simple, @"Dishwasher" }, + { 0x00000076, MTRDeviceTypeClass::Simple, @"Smoke CO Alarm" }, + { 0x00000077, MTRDeviceTypeClass::Simple, @"Cook Surface" }, + { 0x00000078, MTRDeviceTypeClass::Simple, @"Cooktop" }, + { 0x00000079, MTRDeviceTypeClass::Simple, @"Microwave Oven" }, + { 0x0000007A, MTRDeviceTypeClass::Simple, @"Extractor Hood" }, + { 0x0000007B, MTRDeviceTypeClass::Simple, @"Oven" }, + { 0x0000007C, MTRDeviceTypeClass::Simple, @"Laundry Dryer" }, + { 0x00000090, MTRDeviceTypeClass::Simple, @"Network Infrastructure Manager" }, + { 0x00000091, MTRDeviceTypeClass::Simple, @"Thread Border Router" }, + { 0x00000100, MTRDeviceTypeClass::Simple, @"On/Off Light" }, + { 0x00000101, MTRDeviceTypeClass::Simple, @"Dimmable Light" }, + { 0x00000103, MTRDeviceTypeClass::Simple, @"On/Off Light Switch" }, + { 0x00000104, MTRDeviceTypeClass::Simple, @"Dimmer Switch" }, + { 0x00000105, MTRDeviceTypeClass::Simple, @"Color Dimmer Switch" }, + { 0x00000106, MTRDeviceTypeClass::Simple, @"Light Sensor" }, + { 0x00000107, MTRDeviceTypeClass::Simple, @"Occupancy Sensor" }, + { 0x0000010A, MTRDeviceTypeClass::Simple, @"On/Off Plug-in Unit" }, + { 0x0000010B, MTRDeviceTypeClass::Simple, @"Dimmable Plug-in Unit" }, + { 0x0000010C, MTRDeviceTypeClass::Simple, @"Color Temperature Light" }, + { 0x0000010D, MTRDeviceTypeClass::Simple, @"Extended Color Light" }, + { 0x00000202, MTRDeviceTypeClass::Simple, @"Window Covering" }, + { 0x00000203, MTRDeviceTypeClass::Simple, @"Window Covering Controller" }, + { 0x00000300, MTRDeviceTypeClass::Simple, @"Heating/Cooling Unit" }, + { 0x00000301, MTRDeviceTypeClass::Simple, @"Thermostat" }, + { 0x00000302, MTRDeviceTypeClass::Simple, @"Temperature Sensor" }, + { 0x00000303, MTRDeviceTypeClass::Simple, @"Pump" }, + { 0x00000304, MTRDeviceTypeClass::Simple, @"Pump Controller" }, + { 0x00000305, MTRDeviceTypeClass::Simple, @"Pressure Sensor" }, + { 0x00000306, MTRDeviceTypeClass::Simple, @"Flow Sensor" }, + { 0x00000307, MTRDeviceTypeClass::Simple, @"Humidity Sensor" }, + { 0x00000309, MTRDeviceTypeClass::Simple, @"Heat Pump" }, + { 0x0000050C, MTRDeviceTypeClass::Simple, @"EVSE" }, + { 0x0000050D, MTRDeviceTypeClass::Utility, @"Device Energy Management" }, + { 0x0000050F, MTRDeviceTypeClass::Simple, @"Water Heater" }, + { 0x00000510, MTRDeviceTypeClass::Utility, @"Electrical Sensor" }, + { 0x00000840, MTRDeviceTypeClass::Simple, @"Control Bridge" }, + { 0x00000850, MTRDeviceTypeClass::Simple, @"On/Off Sensor" }, }; static_assert(ExtractVendorFromMEI(0xFFF10001) != 0, "Must have class defined for \"Orphan Clusters\" if it's a standard device type"); diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTypeRevisionTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTypeRevisionTests.m new file mode 100644 index 00000000000000..a9f69854917e26 --- /dev/null +++ b/src/darwin/Framework/CHIPTests/MTRDeviceTypeRevisionTests.m @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import +#import + +@interface MTRDeviceTypeRevisionTests : XCTestCase +@end + +@implementation MTRDeviceTypeRevisionTests + +- (void)testInvalidTypeID +{ + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:(id _Nonnull) nil revision:@1]); + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@0xC000 revision:@1]); // type > 0xBFFF + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@0x100000000 revision:@1]); +} + +- (void)testInvalidRevision +{ + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@1 revision:(id _Nonnull) nil]); + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@1 revision:@0]); // < 1 + XCTAssertNil([[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@1 revision:@0x10000]); // > 0xFFFF +} + +- (void)testInitWithStruct +{ + MTRDescriptorClusterDeviceTypeStruct * strukt = [[MTRDescriptorClusterDeviceTypeStruct alloc] init]; + strukt.deviceType = @42; + strukt.revision = @2; + MTRDeviceTypeRevision * typeRev = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeStruct:strukt]; + XCTAssertNotNil(typeRev); + XCTAssertEqualObjects(typeRev, [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@42 revision:@2]); +} + +- (void)testTypeInformation +{ + __auto_type * typeRev = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(MTRDeviceTypeIDTypeRootNodeID) revision:@1]; + XCTAssertNotNil(typeRev); + XCTAssertNotNil(typeRev.typeInformation); + XCTAssertEqualObjects(typeRev.typeInformation.name, @"Root Node"); +} + +- (void)testEqualityAndCopying +{ + __auto_type * a1 = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(MTRDeviceTypeIDTypeMicrowaveOvenID) revision:@1]; + XCTAssertNotNil(a1); + XCTAssertTrue([a1 isEqual:a1]); + XCTAssertTrue([a1 isEqual:[a1 copy]]); + XCTAssertFalse([a1 isEqual:nil]); + XCTAssertFalse([a1 isEqual:@(MTRDeviceTypeIDTypeMicrowaveOvenID)]); + + __auto_type * a2 = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(MTRDeviceTypeIDTypeMicrowaveOvenID) revision:@1]; + XCTAssertNotNil(a2); + XCTAssertEqual(a1.hash, a2.hash); + XCTAssertTrue([a1 isEqual:a2]); + XCTAssertTrue([a2 isEqual:a1]); + + __auto_type * b = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(MTRDeviceTypeIDTypePowerSourceID) revision:@1]; + XCTAssertNotNil(b); + XCTAssertFalse([a1 isEqual:b]); + XCTAssertFalse([b isEqual:a1]); + + __auto_type * c = [[MTRDeviceTypeRevision alloc] initWithDeviceTypeID:@(MTRDeviceTypeIDTypeMicrowaveOvenID) revision:@2]; + XCTAssertNotNil(c); + XCTAssertFalse([c isEqual:a1]); + XCTAssertFalse([a1 isEqual:c]); + XCTAssertFalse([c isEqual:b]); + XCTAssertFalse([b isEqual:c]); +} + +@end diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTypeTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTypeTests.m index 7a41d42b40cb43..1a3f3fc740c722 100644 --- a/src/darwin/Framework/CHIPTests/MTRDeviceTypeTests.m +++ b/src/darwin/Framework/CHIPTests/MTRDeviceTypeTests.m @@ -15,12 +15,9 @@ */ #import - -// system dependencies #import @interface MTRDeviceTypeTests : XCTestCase - @end @implementation MTRDeviceTypeTests @@ -55,7 +52,7 @@ - (void)testKnownUtilityID XCTAssertTrue(deviceType.isUtility); } -- (void)testRootNodeID +- (void)testPowerSource { __auto_type * deviceType = [MTRDeviceType deviceTypeForID:@(MTRDeviceTypeIDTypePowerSourceID)]; XCTAssertNotNil(deviceType); @@ -64,4 +61,24 @@ - (void)testRootNodeID XCTAssertTrue(deviceType.isUtility); } +- (void)testEqualityAndCopying +{ + __auto_type * a1 = [MTRDeviceType deviceTypeForID:@(MTRDeviceTypeIDTypeMicrowaveOvenID)]; + XCTAssertNotNil(a1); + XCTAssertTrue([a1 isEqual:a1]); + XCTAssertFalse([a1 isEqual:nil]); + XCTAssertFalse([a1 isEqual:@(MTRDeviceTypeIDTypeMicrowaveOvenID)]); + + __auto_type * a2 = [MTRDeviceType deviceTypeForID:@(MTRDeviceTypeIDTypeMicrowaveOvenID)]; + XCTAssertNotNil(a2); + XCTAssertEqual(a1.hash, a2.hash); + XCTAssertTrue([a1 isEqual:a2]); + XCTAssertTrue([a2 isEqual:a1]); + + __auto_type * b = [MTRDeviceType deviceTypeForID:@(MTRDeviceTypeIDTypePowerSourceID)]; + XCTAssertNotNil(b); + XCTAssertFalse([a1 isEqual:b]); + XCTAssertFalse([b isEqual:a1]); +} + @end diff --git a/src/darwin/Framework/CHIPTests/MTREndpointInfoTests.m b/src/darwin/Framework/CHIPTests/MTREndpointInfoTests.m new file mode 100644 index 00000000000000..1948639abf2517 --- /dev/null +++ b/src/darwin/Framework/CHIPTests/MTREndpointInfoTests.m @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "MTREndpointInfo_Test.h" + +#import + +@interface MTREndpointInfoTests : XCTestCase +@end + +@implementation MTREndpointInfoTests + +static MTREndpointInfo * MakeEndpoint(NSNumber * endpointID, NSArray * parts) +{ + return [[MTREndpointInfo alloc] initWithEndpointID:endpointID deviceTypes:@[] partsList:parts]; +} + +static NSArray * ChildEndpointIDs(MTREndpointInfo * endpoint) +{ + return [[endpoint.children valueForKey:@"endpointID"] sortedArrayUsingSelector:@selector(compare:)]; +} + +static NSArray * Exclude(NSArray * numbers, NSNumber * numberToExclude) +{ + NSMutableArray * result = [numbers mutableCopy]; + [result removeObject:numberToExclude]; + return result; +} + +- (NSDictionary *)indexEndpoints:(NSArray *)endpoints +{ + NSMutableDictionary * indexed = [[NSMutableDictionary alloc] init]; + for (MTREndpointInfo * endpoint in endpoints) { + indexed[endpoint.endpointID] = endpoint; + } + XCTAssertEqual(indexed.count, endpoints.count, @"Duplicate endpoint IDs"); + return indexed; +} + +- (void)testPopulateChildren +{ + NSDictionary * endpoints = [self indexEndpoints:@[ + MakeEndpoint(@0, @[ @1, @2, @3, @4, @5, @6 ]), // full-family pattern + MakeEndpoint(@1, @[ @2, @3 ]), + MakeEndpoint(@2, @[]), + MakeEndpoint(@3, @[]), + MakeEndpoint(@4, @[ @5, @6 ]), // full-family pattern + MakeEndpoint(@5, @[ @6 ]), + MakeEndpoint(@6, @[]), + ]]; + XCTAssertTrue([MTREndpointInfo populateChildrenForEndpoints:endpoints]); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@0]), (@[ @1, @4 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@1]), (@[ @2, @3 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@2]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@3]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@4]), (@[ @5 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@5]), (@[ @6 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@6]), (@[])); +} + +- (void)testPopulateChildren2 +{ + // Same as testPopulateChildren, but with reversed PartsLists + NSDictionary * endpoints = [self indexEndpoints:@[ + MakeEndpoint(@0, @[ @6, @5, @4, @3, @2, @1 ]), // full-family pattern + MakeEndpoint(@1, @[ @3, @2 ]), + MakeEndpoint(@2, @[]), + MakeEndpoint(@3, @[]), + MakeEndpoint(@4, @[ @6, @5 ]), // full-family pattern + MakeEndpoint(@5, @[ @6 ]), + MakeEndpoint(@6, @[]), + ]]; + XCTAssertTrue([MTREndpointInfo populateChildrenForEndpoints:endpoints]); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@0]), (@[ @1, @4 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@1]), (@[ @2, @3 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@2]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@3]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@4]), (@[ @5 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@5]), (@[ @6 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@6]), (@[])); +} + +- (void)testPopulateChildrenRootOnly +{ + NSDictionary * endpoints = [self indexEndpoints:@[ + MakeEndpoint(@0, @[]), + ]]; + XCTAssertTrue([MTREndpointInfo populateChildrenForEndpoints:endpoints]); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@0]), (@[])); +} + +- (void)testPopulateChildrenInvalidCompositionCycle +{ + NSDictionary * endpoints = [self indexEndpoints:@[ + MakeEndpoint(@0, @[ @1, @2, @3, @4, @5, @6 ]), // full-family pattern + MakeEndpoint(@1, @[ @2, @3 ]), + MakeEndpoint(@2, @[]), + MakeEndpoint(@3, @[]), + MakeEndpoint(@4, @[ @5, @6 ]), // full-family pattern + MakeEndpoint(@5, @[ @6 ]), + MakeEndpoint(@6, @[ @4 ]), // not valid per spec: cycle 4 -> 5 -> 6 -> 4 + ]]; + XCTAssertFalse([MTREndpointInfo populateChildrenForEndpoints:endpoints]); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@0]), (@[ @1, @4 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@1]), (@[ @2, @3 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@2]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@3]), (@[])); + // We make no promises about child lists for endpoints involved in a cycle +} + +- (void)testPopulateChildrenInvalidNonTree +{ + NSDictionary * endpoints = [self indexEndpoints:@[ + MakeEndpoint(@0, @[ @1, @2, @3, @4, @5, @6 ]), // full-family pattern + MakeEndpoint(@1, @[ @2, @3, @6 ]), + MakeEndpoint(@2, @[]), + MakeEndpoint(@3, @[]), + MakeEndpoint(@4, @[ @5, @6 ]), // full-family pattern + MakeEndpoint(@5, @[ @6 ]), + MakeEndpoint(@6, @[]), // not valid per spec: 6 is a child of both 1 and 5 + ]]; + // Note: Not asserting a false return value here, this scenario is currently not detected. + [MTREndpointInfo populateChildrenForEndpoints:endpoints]; + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@0]), (@[ @1, @4 ])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@2]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@3]), (@[])); + XCTAssertEqualObjects(ChildEndpointIDs(endpoints[@4]), (@[ @5 ])); + // Endpoint 6 has multiple parents, so we make no guarantees where (or if) it shows up + XCTAssertEqualObjects(Exclude(ChildEndpointIDs(endpoints[@1]), @6), (@[ @2, @3 ])); + XCTAssertEqualObjects(Exclude(ChildEndpointIDs(endpoints[@5]), @6), (@[])); +} + +@end diff --git a/src/darwin/Framework/CHIPTests/MTRPairingTests.m b/src/darwin/Framework/CHIPTests/MTRPairingTests.m index 675c04156e62ae..0447d58a5a3fc3 100644 --- a/src/darwin/Framework/CHIPTests/MTRPairingTests.m +++ b/src/darwin/Framework/CHIPTests/MTRPairingTests.m @@ -1,6 +1,5 @@ -/* - * - * Copyright (c) 2022 Project CHIP Authors +/** + * Copyright (c) 2022-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -107,6 +106,7 @@ @interface MTRPairingTestControllerDelegate : NSObject attestationDelegate; @property (nonatomic, nullable) NSNumber * failSafeExtension; +@property (nonatomic) BOOL shouldReadEndpointInformation; @property (nullable) NSError * commissioningCompleteError; @end @@ -131,6 +131,7 @@ - (void)controller:(MTRDeviceController *)controller commissioningSessionEstabli __auto_type * params = [[MTRCommissioningParameters alloc] init]; params.deviceAttestationDelegate = self.attestationDelegate; params.failSafeTimeout = self.failSafeExtension; + params.readEndpointInformation = self.shouldReadEndpointInformation; NSError * commissionError = nil; XCTAssertTrue([controller commissionNodeWithID:@(sDeviceId) commissioningParams:params error:&commissionError], @@ -139,6 +140,30 @@ - (void)controller:(MTRDeviceController *)controller commissioningSessionEstabli // Keep waiting for onCommissioningComplete } +- (void)controller:(MTRDeviceController *)controller readCommissioneeInfo:(MTRCommissioneeInfo *)info +{ + XCTAssertNotNil(info.productIdentity); + XCTAssertEqualObjects(info.productIdentity.vendorID, /* Test Vendor 1 */ @0xFFF1); + + if (self.shouldReadEndpointInformation) { + XCTAssertNotNil(info.endpointsById); + XCTAssertNotNil(info.rootEndpoint); + XCTAssertGreaterThanOrEqual(info.rootEndpoint.children.count, 2); // at least one application endpoint + for (MTREndpointInfo * endpoint in info.endpointsById.allValues) { + XCTAssertGreaterThanOrEqual(endpoint.deviceTypes.count, 1); + XCTAssertNotNil(endpoint.children); + XCTAssertNotNil(endpoint.partsList); + XCTAssertGreaterThanOrEqual(endpoint.partsList.count, endpoint.children.count); + for (MTREndpointInfo * child in endpoint.children) { + XCTAssertTrue([endpoint.partsList containsObject:child.endpointID]); + } + } + } else { + XCTAssertNil(info.endpointsById); + XCTAssertNil(info.rootEndpoint); + } +} + - (void)controller:(MTRDeviceController *)controller commissioningComplete:(NSError * _Nullable)error { self.commissioningCompleteError = error; @@ -152,20 +177,20 @@ @interface MTRPairingTestMonitoringControllerDelegate : NSObject ", self, MTR_YES_NO(_statusUpdateCalled), MTR_YES_NO(_commissioningSessionEstablishmentDoneCalled), MTR_YES_NO(_commissioningCompleteCalled), MTR_YES_NO(_readCommissioningInfoCalled)]; + return [NSString stringWithFormat:@"", self, MTR_YES_NO(_statusUpdateCalled), MTR_YES_NO(_commissioningSessionEstablishmentDoneCalled), MTR_YES_NO(_commissioningCompleteCalled), MTR_YES_NO(_readCommissioneeInfoCalled)]; } - (void)_checkIfAllCallbacksCalled { if (self.allCallbacksCalledExpectation) { - if (self.statusUpdateCalled && self.commissioningSessionEstablishmentDoneCalled && self.commissioningCompleteCalled && self.readCommissioningInfoCalled) { + if (self.statusUpdateCalled && self.commissioningSessionEstablishmentDoneCalled && self.commissioningCompleteCalled && self.readCommissioneeInfoCalled) { [self.allCallbacksCalledExpectation fulfill]; self.allCallbacksCalledExpectation = nil; } @@ -193,11 +218,12 @@ - (void)controller:(MTRDeviceController *)controller [self _checkIfAllCallbacksCalled]; } -- (void)controller:(MTRDeviceController *)controller readCommissioningInfo:(MTRProductIdentity *)info +- (void)controller:(MTRDeviceController *)controller readCommissioneeInfo:(MTRCommissioneeInfo *)info { - self.readCommissioningInfoCalled = YES; + self.readCommissioneeInfoCalled = YES; [self _checkIfAllCallbacksCalled]; } + @end @interface MTRPairingTests : MTRTestCase @@ -318,7 +344,7 @@ - (void)doPairingTestWithAttestationDelegate:(id)a XCTAssertTrue(monitoringControllerDelegate.statusUpdateCalled); XCTAssertTrue(monitoringControllerDelegate.commissioningSessionEstablishmentDoneCalled); XCTAssertTrue(monitoringControllerDelegate.commissioningCompleteCalled); - XCTAssertTrue(monitoringControllerDelegate.readCommissioningInfoCalled); + XCTAssertTrue(monitoringControllerDelegate.readCommissioneeInfoCalled); [sController removeDeviceControllerDelegate:monitoringControllerDelegate]; } @@ -454,4 +480,29 @@ - (void)test008_pairingAfterCancellation_DeviceAttestationVerification XCTAssertTrue(delegateCalled); } +- (void)test009_PairWithReadingEndpointInformation +{ + [self startServerApp]; + + XCTestExpectation * expectation = [self expectationWithDescription:@"Commissioning Complete"]; + __auto_type * controllerDelegate = [[MTRPairingTestControllerDelegate alloc] initWithExpectation:expectation + attestationDelegate:nil + failSafeExtension:nil]; + + // Endpoint info is validated by MTRPairingTestControllerDelegate + controllerDelegate.shouldReadEndpointInformation = YES; + + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.pairing", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); + [sController setDeviceControllerDelegate:controllerDelegate queue:callbackQueue]; + self.controllerDelegate = controllerDelegate; + + NSError * error; + __auto_type * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:kOnboardingPayload error:&error]; + XCTAssertTrue([sController setupCommissioningSessionWithPayload:payload newNodeID:@(++sDeviceId) error:&error]); + XCTAssertNil(error); + + [self waitForExpectations:@[ expectation ] timeout:kPairingTimeoutInSeconds]; + XCTAssertNil(controllerDelegate.commissioningCompleteError); +} + @end diff --git a/src/darwin/Framework/CHIPTests/MTRProductIdentityTests.m b/src/darwin/Framework/CHIPTests/MTRProductIdentityTests.m new file mode 100644 index 00000000000000..4974a4077f4563 --- /dev/null +++ b/src/darwin/Framework/CHIPTests/MTRProductIdentityTests.m @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import +#import + +@interface MTRProductIdentityTests : XCTestCase + +@end + +@implementation MTRProductIdentityTests + +- (void)testEqualityAndCopying +{ + MTRProductIdentity * a1 = [[MTRProductIdentity alloc] initWithVendorID:@1 productID:@1]; + XCTAssertNotNil(a1); + XCTAssertTrue([a1 isEqual:a1]); + XCTAssertTrue([a1 isEqual:[a1 copy]]); + XCTAssertFalse([a1 isEqual:nil]); + XCTAssertFalse([a1 isEqual:@1]); + + MTRProductIdentity * a2 = [[MTRProductIdentity alloc] initWithVendorID:@1 productID:@1]; + XCTAssertNotNil(a2); + XCTAssertTrue([a1 isEqual:a2]); + XCTAssertTrue([a2 isEqual:a1]); + + MTRProductIdentity * b = [[MTRProductIdentity alloc] initWithVendorID:@1 productID:@555]; + XCTAssertNotNil(b); + XCTAssertFalse([b isEqual:a1]); + XCTAssertFalse([a1 isEqual:b]); + + MTRProductIdentity * c = [[MTRProductIdentity alloc] initWithVendorID:@555 productID:@1]; + XCTAssertNotNil(c); + XCTAssertFalse([c isEqual:a1]); + XCTAssertFalse([a1 isEqual:c]); + XCTAssertFalse([c isEqual:b]); + XCTAssertFalse([b isEqual:c]); +} + +@end diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index 77cc1c03187bd9..76c3f928a22946 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -116,6 +116,16 @@ 3D843717294979230070D20A /* MTRClusters_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D843715294979230070D20A /* MTRClusters_Internal.h */; }; 3D843756294AD25A0070D20A /* MTRCertificateInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D843754294AD25A0070D20A /* MTRCertificateInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3D843757294AD25A0070D20A /* MTRCertificateInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D843755294AD25A0070D20A /* MTRCertificateInfo.mm */; }; + 3D9F2FBE2D10DB4F003CA2BB /* MTRProductIdentity.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D9F2FBC2D10DB4F003CA2BB /* MTRProductIdentity.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3D9F2FBF2D10DB4F003CA2BB /* MTRProductIdentity.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FBD2D10DB4F003CA2BB /* MTRProductIdentity.mm */; }; + 3D9F2FC12D10DCA2003CA2BB /* MTRProductIdentityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FC02D10DCA2003CA2BB /* MTRProductIdentityTests.m */; }; + 3D9F2FC32D10E207003CA2BB /* MTRDeviceTypeRevisionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FC22D10E207003CA2BB /* MTRDeviceTypeRevisionTests.m */; }; + 3D9F2FC62D10EA11003CA2BB /* MTREndpointInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FC52D10EA11003CA2BB /* MTREndpointInfo.mm */; }; + 3D9F2FC72D10EA11003CA2BB /* MTREndpointInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D9F2FC42D10EA11003CA2BB /* MTREndpointInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3D9F2FC92D1105BA003CA2BB /* MTREndpointInfo_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D9F2FC82D1105BA003CA2BB /* MTREndpointInfo_Internal.h */; }; + 3D9F2FCE2D112295003CA2BB /* MTRAttributeTLVValueDecoder_Internal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FCD2D112295003CA2BB /* MTRAttributeTLVValueDecoder_Internal.mm */; }; + 3D9F2FD02D11FE90003CA2BB /* MTREndpointInfoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D9F2FCF2D11FE90003CA2BB /* MTREndpointInfoTests.m */; }; + 3D9F2FD22D11FF27003CA2BB /* MTREndpointInfo_Test.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D9F2FD12D11FF27003CA2BB /* MTREndpointInfo_Test.h */; }; 3DA1A3552ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DA1A3522ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.h */; }; 3DA1A3562ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3DA1A3532ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.mm */; }; 3DA1A3582ABABF6A004F0BB9 /* MTRAsyncWorkQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DA1A3572ABABF69004F0BB9 /* MTRAsyncWorkQueueTests.m */; }; @@ -611,6 +621,16 @@ 3D84372F294984AF0070D20A /* command_completion_type.zapt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = command_completion_type.zapt; sourceTree = ""; }; 3D843754294AD25A0070D20A /* MTRCertificateInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRCertificateInfo.h; sourceTree = ""; }; 3D843755294AD25A0070D20A /* MTRCertificateInfo.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRCertificateInfo.mm; sourceTree = ""; }; + 3D9F2FBC2D10DB4F003CA2BB /* MTRProductIdentity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRProductIdentity.h; sourceTree = ""; }; + 3D9F2FBD2D10DB4F003CA2BB /* MTRProductIdentity.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRProductIdentity.mm; sourceTree = ""; }; + 3D9F2FC02D10DCA2003CA2BB /* MTRProductIdentityTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTRProductIdentityTests.m; sourceTree = ""; }; + 3D9F2FC22D10E207003CA2BB /* MTRDeviceTypeRevisionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTRDeviceTypeRevisionTests.m; sourceTree = ""; }; + 3D9F2FC42D10EA11003CA2BB /* MTREndpointInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTREndpointInfo.h; sourceTree = ""; }; + 3D9F2FC52D10EA11003CA2BB /* MTREndpointInfo.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTREndpointInfo.mm; sourceTree = ""; }; + 3D9F2FC82D1105BA003CA2BB /* MTREndpointInfo_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTREndpointInfo_Internal.h; sourceTree = ""; }; + 3D9F2FCD2D112295003CA2BB /* MTRAttributeTLVValueDecoder_Internal.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRAttributeTLVValueDecoder_Internal.mm; sourceTree = ""; }; + 3D9F2FCF2D11FE90003CA2BB /* MTREndpointInfoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTREndpointInfoTests.m; sourceTree = ""; }; + 3D9F2FD12D11FF27003CA2BB /* MTREndpointInfo_Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTREndpointInfo_Test.h; sourceTree = ""; }; 3DA1A3522ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRAsyncWorkQueue.h; sourceTree = ""; }; 3DA1A3532ABAB3B4004F0BB9 /* MTRAsyncWorkQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRAsyncWorkQueue.mm; sourceTree = ""; }; 3DA1A3572ABABF69004F0BB9 /* MTRAsyncWorkQueueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRAsyncWorkQueueTests.m; sourceTree = ""; }; @@ -1339,8 +1359,6 @@ 51D0B1362B618CC6006E3511 /* MTRServerAttribute.h */, 51D0B1372B618CC6006E3511 /* MTRServerAttribute.mm */, 514C79FF2B64223400DD6D7B /* MTRServerAttribute_Internal.h */, - 51D0B13A2B61B2F2006E3511 /* MTRDeviceTypeRevision.h */, - 51D0B13B2B61B2F2006E3511 /* MTRDeviceTypeRevision.mm */, 51D0B1262B617246006E3511 /* MTRServerEndpoint.h */, 51D0B1252B617246006E3511 /* MTRServerEndpoint.mm */, 514C7A002B64223400DD6D7B /* MTRServerEndpoint_Internal.h */, @@ -1451,6 +1469,7 @@ 27A53C1527FBC6920053F131 /* MTRAttestationTrustStoreBridge.h */, 27A53C1627FBC6920053F131 /* MTRAttestationTrustStoreBridge.mm */, 513DDB852761F69300DAA01A /* MTRAttributeTLVValueDecoder_Internal.h */, + 3D9F2FCD2D112295003CA2BB /* MTRAttributeTLVValueDecoder_Internal.mm */, 75B765C02A1D71BC0014719B /* MTRAttributeSpecifiedCheck.h */, 51EF279E2A2A3EB100E33F75 /* MTRBackwardsCompatShims.h */, 510470FA2A2F7DF60053EA7E /* MTRBackwardsCompatShims.mm */, @@ -1544,6 +1563,12 @@ 51F522692AE70761000C4050 /* MTRDeviceTypeMetadata.h */, 5109E9B22CB8B5DF0006884B /* MTRDeviceType.h */, 5109E9B32CB8B5DF0006884B /* MTRDeviceType.mm */, + 51D0B13A2B61B2F2006E3511 /* MTRDeviceTypeRevision.h */, + 51D0B13B2B61B2F2006E3511 /* MTRDeviceTypeRevision.mm */, + 3D9F2FC42D10EA11003CA2BB /* MTREndpointInfo.h */, + 3D9F2FD12D11FF27003CA2BB /* MTREndpointInfo_Test.h */, + 3D9F2FC82D1105BA003CA2BB /* MTREndpointInfo_Internal.h */, + 3D9F2FC52D10EA11003CA2BB /* MTREndpointInfo.mm */, 5129BCFC26A9EE3300122DDF /* MTRError.h */, B2E0D7AB245B0B5C003C5B48 /* MTRError_Internal.h */, 51578AEA2D0B9DC0001716FF /* MTRError_Testable.h */, @@ -1574,6 +1599,8 @@ 998F287026D56940001846C6 /* MTRP256KeypairBridge.mm */, 2C8C8FBD253E0C2100797F05 /* MTRPersistentStorageDelegateBridge.h */, 2C8C8FBF253E0C2100797F05 /* MTRPersistentStorageDelegateBridge.mm */, + 3D9F2FBC2D10DB4F003CA2BB /* MTRProductIdentity.h */, + 3D9F2FBD2D10DB4F003CA2BB /* MTRProductIdentity.mm */, 514654482A72F9DF00904E61 /* MTRDemuxingStorage.mm */, 5146544A2A72F9F500904E61 /* MTRDemuxingStorage.h */, 51E95DF92A78443C00A434F0 /* MTRSessionResumptionStorageBridge.h */, @@ -1623,7 +1650,9 @@ 5AE6D4E327A99041001F2493 /* MTRDeviceTests.m */, 75B326A12BCF12E900E17C4E /* MTRDeviceConnectivityMonitorTests.m */, 5109E9B62CB8B83D0006884B /* MTRDeviceTypeTests.m */, + 3D9F2FC22D10E207003CA2BB /* MTRDeviceTypeRevisionTests.m */, 51D9CB0A2BA37DCE0049D6DB /* MTRDSTOffsetTests.m */, + 3D9F2FCF2D11FE90003CA2BB /* MTREndpointInfoTests.m */, 3D0C484A29DA4FA0006D811F /* MTRErrorTests.m */, 51578AE82D0B9B1D001716FF /* MTRErrorMappingTests.m */, 5173A47829C0E82300F67F48 /* MTRFabricInfoTests.m */, @@ -1632,6 +1661,7 @@ 5142E39729D377F000A206F0 /* MTROTAProviderTests.m */, 51742B4D29CB6B88009974FE /* MTRPairingTests.m */, 51E95DF72A78110900A434F0 /* MTRPerControllerStorageTests.m */, + 3D9F2FC02D10DCA2003CA2BB /* MTRProductIdentityTests.m */, 51D0B1292B61766F006E3511 /* MTRServerEndpointTests.m */, 3D4733AE2BDF1B80003DC19B /* MTRSetupPayloadTests.m */, 519498312A25581C00B3BABE /* MTRSetupPayloadInitializationTests.m */, @@ -1829,6 +1859,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 3D9F2FBE2D10DB4F003CA2BB /* MTRProductIdentity.h in Headers */, 51D0B1282B617246006E3511 /* MTRServerEndpoint.h in Headers */, 51565CB62A7B0D6600469F18 /* MTRDeviceControllerParameters.h in Headers */, 51565CB42A7AD78D00469F18 /* MTRDeviceControllerStorageDelegate.h in Headers */, @@ -1913,6 +1944,7 @@ 514C7A012B64223400DD6D7B /* MTRServerAttribute_Internal.h in Headers */, 3DFCB32C29678C9500332B35 /* MTRConversion.h in Headers */, 5A60370827EA1FF60020DB79 /* MTRClusterStateCacheContainer+XPC.h in Headers */, + 3D9F2FC92D1105BA003CA2BB /* MTREndpointInfo_Internal.h in Headers */, 5ACDDD7E27CD3F3A00EFD68A /* MTRClusterStateCacheContainer_Internal.h in Headers */, 5136661328067D550025EDAE /* MTRDeviceController_Internal.h in Headers */, 998F286D26D55E10001846C6 /* MTRKeypair.h in Headers */, @@ -1941,6 +1973,7 @@ 9B231B042C62EF650030EB37 /* (null) in Headers */, 515BE4ED2B72C0C5000BC1FD /* MTRUnfairLock.h in Headers */, 998F286F26D55EC5001846C6 /* MTRP256KeypairBridge.h in Headers */, + 3D9F2FC72D10EA11003CA2BB /* MTREndpointInfo.h in Headers */, CF3B63CF2CA31E71003C1C87 /* MTROTAImageTransferHandler.h in Headers */, 2C222ADF255C811800E446B9 /* MTRBaseDevice_Internal.h in Headers */, 514C7A022B64223400DD6D7B /* MTRServerEndpoint_Internal.h in Headers */, @@ -1958,6 +1991,7 @@ 7592BCF42CBEE98C00EB74A0 /* EmberMetadata.h in Headers */, 7592BCF52CBEE98C00EB74A0 /* CodegenDataModelProvider.h in Headers */, 7596A84828762783004DAE0E /* MTRAsyncCallbackWorkQueue.h in Headers */, + 3D9F2FD22D11FF27003CA2BB /* MTREndpointInfo_Test.h in Headers */, 5A7947E527C0129F00434CF2 /* MTRDeviceController+XPC.h in Headers */, 51E95DFB2A78443C00A434F0 /* MTRSessionResumptionStorageBridge.h in Headers */, B2E0D7B4245B0B5C003C5B48 /* MTRError_Internal.h in Headers */, @@ -2290,6 +2324,7 @@ CF3B63D12CA31E71003C1C87 /* MTROTAImageTransferHandler.mm in Sources */, 51D0B12F2B617800006E3511 /* MTRAccessGrant.mm in Sources */, 88E6C9482B6334ED001A1FE0 /* MTRMetrics.mm in Sources */, + 3D9F2FBF2D10DB4F003CA2BB /* MTRProductIdentity.mm in Sources */, 1ED276E226C5812A00547A89 /* MTRCluster.mm in Sources */, 9BFE5D512C6D3075007D4319 /* MTRDeviceController_XPC.mm in Sources */, 514A98B12CD98C5E000EF4FD /* MTRAttributeValueWaiter.mm in Sources */, @@ -2306,12 +2341,14 @@ 5ACDDD7D27CD16D200EFD68A /* MTRClusterStateCacheContainer.mm in Sources */, 75B3269E2BCDB9EA00E17C4E /* MTRDeviceConnectivityMonitor.mm in Sources */, 7534D1802CF8CE2000F64654 /* DefaultAttributePersistenceProvider.cpp in Sources */, + 3D9F2FC62D10EA11003CA2BB /* MTREndpointInfo.mm in Sources */, 9B5CCB5C2C6EC890009DD99B /* MTRDevice_XPC.mm in Sources */, 513DDB8A2761F6F900DAA01A /* MTRAttributeTLVValueDecoder.mm in Sources */, 5117DD3829A931AE00FFA1AA /* MTROperationalBrowser.mm in Sources */, 514C79F02B62ADDA00DD6D7B /* descriptor.cpp in Sources */, 5109E9B42CB8B5DF0006884B /* MTRDeviceType.mm in Sources */, 3D843757294AD25A0070D20A /* MTRCertificateInfo.mm in Sources */, + 3D9F2FCE2D112295003CA2BB /* MTRAttributeTLVValueDecoder_Internal.mm in Sources */, 5A7947E427C0129600434CF2 /* MTRDeviceController+XPC.mm in Sources */, 7592BD0B2CC6BCC300EB74A0 /* EmberAttributeDataBuffer.cpp in Sources */, 5A6FEC9027B563D900F25F42 /* MTRDeviceControllerOverXPC.mm in Sources */, @@ -2368,7 +2405,9 @@ 518D3F832AA132DC008E0007 /* MTRTestPerControllerStorage.m in Sources */, 51339B1F2A0DA64D00C798C1 /* MTRCertificateValidityTests.m in Sources */, 5173A47929C0E82300F67F48 /* MTRFabricInfoTests.m in Sources */, + 3D9F2FC12D10DCA2003CA2BB /* MTRProductIdentityTests.m in Sources */, 75B326A22BCF12E900E17C4E /* MTRDeviceConnectivityMonitorTests.m in Sources */, + 3D9F2FD02D11FE90003CA2BB /* MTREndpointInfoTests.m in Sources */, 5143851E2A65885500EDC8E6 /* MTRSwiftPairingTests.swift in Sources */, 75B0D01E2B71B47F002074DD /* MTRDeviceTestDelegate.m in Sources */, 3D0C484B29DA4FA0006D811F /* MTRErrorTests.m in Sources */, @@ -2381,6 +2420,7 @@ 51F9F9D52BF7A9EE00FEA0E2 /* MTRTestCase+ServerAppRunner.m in Sources */, 517BF3F3282B62CB00A8B7DB /* MTRCertificateTests.m in Sources */, 5142E39829D377F000A206F0 /* MTROTAProviderTests.m in Sources */, + 3D9F2FC32D10E207003CA2BB /* MTRDeviceTypeRevisionTests.m in Sources */, 51E0FC102ACBBF230001E197 /* MTRSwiftDeviceTests.swift in Sources */, 3D4733AF2BDF1B80003DC19B /* MTRSetupPayloadTests.m in Sources */, 5109E9B72CB8B83D0006884B /* MTRDeviceTypeTests.m in Sources */, diff --git a/src/lib/support/ScopedBuffer.h b/src/lib/support/ScopedBuffer.h index 7a3c0aaca5902c..923c532e15b003 100644 --- a/src/lib/support/ScopedBuffer.h +++ b/src/lib/support/ScopedBuffer.h @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -218,11 +219,12 @@ class ScopedMemoryBufferWithSize : public ScopedMemoryBuffer return *this; } - ~ScopedMemoryBufferWithSize() { mCount = 0; } - // return the size as count of elements inline size_t AllocatedSize() const { return mCount; } + chip::Span Span() { return chip::Span(this->Get(), AllocatedSize()); } + chip::Span Span() const { return chip::Span(this->Get(), AllocatedSize()); } + void Free() { mCount = 0;