diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index 8ff85e39f9c49c..b7c2e1395dd6ac 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -14,6 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include +#include +#include #import #import @@ -129,6 +132,10 @@ @implementation MTRDeviceController { std::atomic> _storedCompressedFabricID; MTRP256KeypairBridge _signingKeypairBridge; MTRP256KeypairBridge _operationalKeypairBridge; + + NSNumber * _fabricID; + NSNumber * _nodeID; + NSData * _rootPublicKey; } - (os_unfair_lock_t)deviceMapLock @@ -304,6 +311,9 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory _storedFabricIndex = chip::kUndefinedFabricIndex; _storedCompressedFabricID = std::nullopt; + _nodeID = nil; + _fabricID = nil; + _rootPublicKey = nil; _storageBehaviorConfiguration = storageBehaviorConfiguration; } @@ -330,6 +340,26 @@ - (NSUInteger)shutdownPrecheck return assertionCount; } +- (BOOL)matchesPendingShutdownWithParams:(MTRDeviceControllerParameters *)parameters +{ + if (!parameters.operationalCertificate || !parameters.rootCertificate) { + return FALSE; + } + NSNumber *nodeID = [MTRDeviceControllerParameters nodeIDFromNOC:parameters.operationalCertificate]; + NSNumber *fabricID = [MTRDeviceControllerParameters nodeIDFromNOC:parameters.operationalCertificate]; + NSData *publicKey = [MTRDeviceControllerParameters publicKeyFromCertificate:parameters.rootCertificate]; + + __block BOOL matches = FALSE; + dispatch_sync(_chipWorkQueue, ^{ + matches = self.keepRunningAssertionCounter > 0 && + self.shutdownPending && + MTREqualObjects(nodeID, self->_nodeID) && + MTREqualObjects(fabricID, self->_fabricID) && + MTREqualObjects(publicKey, self->_rootPublicKey); + }); + return matches; +} + - (void)addRunAssertion { dispatch_sync(_chipWorkQueue, ^{ @@ -356,6 +386,13 @@ - (void)removeRunAssertion; } } +- (void)clearPendingShutdown +{ + dispatch_sync(_chipWorkQueue, ^{ + self.shutdownPending = FALSE; + }); +} + - (void)shutdown { NSUInteger assertionCount = [self shutdownPrecheck]; @@ -424,6 +461,9 @@ - (void)shutDownCppController // shuts down. _storedFabricIndex = chip::kUndefinedFabricIndex; _storedCompressedFabricID = std::nullopt; + _nodeID = nil; + _fabricID = nil; + _rootPublicKey = nil; delete commissionerToShutDown; if (_operationalCredentialsDelegate != nil) { _operationalCredentialsDelegate->SetDeviceCommissioner(nullptr); @@ -665,6 +705,14 @@ - (BOOL)startup:(MTRDeviceControllerStartupParamsInternal *)startupParams self->_storedFabricIndex = fabricIdx; self->_storedCompressedFabricID = _cppCommissioner->GetCompressedFabricId(); + + chip::Crypto::P256PublicKey rootPublicKey; + if (_cppCommissioner->GetRootPublicKey(rootPublicKey) == CHIP_NO_ERROR) { + self->_rootPublicKey = [NSData dataWithBytes:rootPublicKey.Bytes() length:rootPublicKey.Length()]; + self->_nodeID = @(_cppCommissioner->GetNodeId()); + self->_fabricID = @(_cppCommissioner->GetFabricId()); + } + commissionerInitialized = YES; MTR_LOG("%@ startup succeeded for nodeID 0x%016llX", self, self->_cppCommissioner->GetNodeId()); diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm index 5b089b392074e6..ae2ca11efb2772 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm @@ -15,6 +15,7 @@ */ #import "MTRDeviceControllerFactory.h" +#include #import "MTRDeviceControllerFactory_Internal.h" #import @@ -1133,12 +1134,31 @@ - (void)operationalInstanceAdded:(chip::PeerId &)operationalID } } +- (nullable MTRDeviceController *)_findControllerMatchingParams:(MTRDeviceControllerParameters *)parameters +{ + std::lock_guard lock(_controllersLock); + for (MTRDeviceController *controller in _controllers) { + if ([controller matchesPendingShutdownWithParams:parameters]) { + MTR_LOG("%@ Found existing controller %@ that is pending shutdown and matching parameters, re-using it", self, controller); + [controller clearPendingShutdown]; + return controller; + } + } + return nil; +} + - (nullable MTRDeviceController *)initializeController:(MTRDeviceController *)controller withParameters:(MTRDeviceControllerParameters *)parameters error:(NSError * __autoreleasing *)error { [self _assertCurrentQueueIsNotMatterQueue]; + // If there is a controller already running with matching parameters, re-use it + MTRDeviceController *existingController = [self _findControllerMatchingParams:parameters]; + if (existingController) { + return existingController; + } + return [self _startDeviceController:controller startupParams:parameters fabricChecker:^MTRDeviceControllerStartupParamsInternal *( diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm index c5923ad4e54619..66792d4cedcf0f 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm @@ -15,6 +15,8 @@ */ #import "MTRDeviceControllerStartupParams.h" +#include +#include #import "MTRCertificates.h" #import "MTRConversion.h" #import "MTRDeviceControllerStartupParams_Internal.h" @@ -306,6 +308,29 @@ - (void)setOTAProviderDelegate:(id)otaProviderDelegate q _otaProviderDelegateQueue = queue; } ++ (nullable NSNumber *)nodeIDFromNOC:(MTRCertificateDERBytes)noc +{ + NSNumber *nodeID = nil; + ExtractNodeIDFromNOC(noc, &nodeID); + return nodeID; +} + ++ (nullable NSNumber *)fabricIDFromNOC:(MTRCertificateDERBytes)noc +{ + NSNumber *fabricID = nil; + ExtractFabricIDFromNOC(noc, &fabricID); + return fabricID; +} + ++ (NSData *)publicKeyFromCertificate:(MTRCertificateDERBytes)certificate +{ + Crypto::P256PublicKey pubKey; + if (ExtractPubkeyFromX509Cert(AsByteSpan(certificate), pubKey) != CHIP_NO_ERROR) { + return nil; + } + return [NSData dataWithBytes:pubKey.Bytes() length:pubKey.Length()]; +} + @end @implementation MTRDeviceControllerExternalCertificateParameters diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h index 6b8c762633578a..d5e35a63022a7c 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h @@ -85,6 +85,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, readonly, nullable) id otaProviderDelegate; @property (nonatomic, strong, readonly, nullable) dispatch_queue_t otaProviderDelegateQueue; ++ (nullable NSNumber *)nodeIDFromNOC:(MTRCertificateDERBytes)noc; ++ (nullable NSNumber *)fabricIDFromNOC:(MTRCertificateDERBytes)noc; ++ (NSData *)publicKeyFromCertificate:(MTRCertificateDERBytes)certificate; + @end @interface MTRDeviceControllerStartupParamsInternal : MTRDeviceControllerStartupParams diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h index 29a694642dad02..5c4caa49cdd00f 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h @@ -45,6 +45,7 @@ #import #import +@class MTRDeviceControllerParameters; @class MTRDeviceControllerStartupParamsInternal; @class MTRDeviceControllerFactory; @class MTRDevice; @@ -314,6 +315,16 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSUInteger)shutdownPrecheck; +/** + * This method returns TRUE if this controller matches the fabric reference and node ID as listed in the parameters. + */ +- (BOOL)matchesPendingShutdownWithParams:(MTRDeviceControllerParameters *)parameters; + +/** + * Clear any pending shutdown request. + */ +- (void)clearPendingShutdown; + @end /**