From 17628502713205e0d2a11349cdb354d4c75c37bb Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 9 Nov 2022 08:09:37 -0500 Subject: [PATCH] Address API review issues in MTRDeviceController. (#23512) This is a re-landing of PR #22596 but with changes made for backwards compat. * Make MTRBaseDevice creation synchronous. This requires updates to MTRBaseDeviceOverXPC to do the possible async getting of the controller id it needs during its async operations, not when getting the device object. * Deprecate pairDevice methods. * Rename "commissionDevice" to "commissionNodeWithID". * Rename "stopDevicePairing" to "cancelCommissioningForNodeID" and document. * Rename "getDeviceBeingCommissioned" to "getDeviceBeingCommissionedWithNodeID". * Various documentation improvements. * Fix signature of computePaseVerifier. The header changes not accompanied by backwards-compat shims are OK for the following reasons: * In MTRDeviceController.h, the change to isRunning is binary and source compatible. * MTRDeviceControllerOverXPC_Internal.h is not public API. * MTRDeviceController_Internal.h is not public API. * MTRDeviceOverXPC.h is not public API. * The changes to MTRSetupPayload.h are backward-compatible. --- .../commands/clusters/ModelCommandBridge.mm | 28 +- .../commands/pairing/PairingCommandBridge.mm | 71 +- .../commands/pairing/PairingDelegateBridge.mm | 2 +- .../commands/tests/TestCommandBridge.h | 26 +- src/darwin/Framework/CHIP/MTRBaseDevice.h | 9 + src/darwin/Framework/CHIP/MTRBaseDevice.mm | 7 + .../Framework/CHIP/MTRDeviceController.h | 137 +- .../Framework/CHIP/MTRDeviceController.mm | 388 +++--- .../CHIP/MTRDeviceControllerOverXPC.m | 87 +- .../MTRDeviceControllerOverXPC_Internal.h | 5 + .../CHIP/MTRDeviceController_Internal.h | 7 + src/darwin/Framework/CHIP/MTRDeviceOverXPC.h | 8 +- src/darwin/Framework/CHIP/MTRDeviceOverXPC.m | 289 +++-- src/darwin/Framework/CHIP/MTRSetupPayload.h | 3 + src/darwin/Framework/CHIP/MTRSetupPayload.mm | 24 +- .../Framework/CHIPTests/MTRControllerTests.m | 5 - .../Framework/CHIPTests/MTRDeviceTests.m | 40 +- .../CHIPTests/MTRXPCListenerSampleTests.m | 279 ++-- .../Framework/CHIPTests/MTRXPCProtocolTests.m | 1128 ++++++++--------- 19 files changed, 1265 insertions(+), 1278 deletions(-) diff --git a/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.mm b/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.mm index a21e6e201a2419..97b7c09806d200 100644 --- a/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.mm +++ b/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.mm @@ -25,31 +25,15 @@ CHIP_ERROR ModelCommand::RunCommand() { - dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip-tool.command", DISPATCH_QUEUE_SERIAL); - MTRDeviceController * commissioner = CurrentCommissioner(); ChipLogProgress(chipTool, "Sending command to node 0x" ChipLogFormatX64, ChipLogValueX64(mNodeId)); - [commissioner getBaseDevice:mNodeId - queue:callbackQueue - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - if (error != nil) { - SetCommandExitStatus(error, "Error getting connected device"); - return; - } - - CHIP_ERROR err; - if (device == nil) { - err = CHIP_ERROR_INTERNAL; - } else { - err = SendCommand(device, mEndPointId); - } + auto * device = [MTRBaseDevice deviceWithNodeID:@(mNodeId) controller:commissioner]; + CHIP_ERROR err = SendCommand(device, mEndPointId); - if (err != CHIP_NO_ERROR) { - ChipLogError(chipTool, "Error: %s", chip::ErrorStr(err)); - SetCommandExitStatus(err); - return; - } - }]; + if (err != CHIP_NO_ERROR) { + ChipLogError(chipTool, "Error: %s", chip::ErrorStr(err)); + return err; + } return CHIP_NO_ERROR; } diff --git a/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm b/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm index ae27e63d53d6fd..a05e9ab2eddf7e 100644 --- a/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm +++ b/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm @@ -101,47 +101,32 @@ { dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip-tool.command", DISPATCH_QUEUE_SERIAL); MTRDeviceController * commissioner = CurrentCommissioner(); - [commissioner - getBaseDevice:mNodeId - queue:callbackQueue - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - CHIP_ERROR err = CHIP_NO_ERROR; - if (error) { - err = MTRErrorToCHIPErrorCode(error); - LogNSError("Error: ", error); - SetCommandExitStatus(err); - } else if (device == nil) { - ChipLogError(chipTool, "Error: %s", chip::ErrorStr(CHIP_ERROR_INTERNAL)); - SetCommandExitStatus(CHIP_ERROR_INTERNAL); - } else { - ChipLogProgress(chipTool, "Attempting to unpair device %llu", mNodeId); - MTRBaseClusterOperationalCredentials * opCredsCluster = - [[MTRBaseClusterOperationalCredentials alloc] initWithDevice:device endpointID:@(0) queue:callbackQueue]; - [opCredsCluster - readAttributeCurrentFabricIndexWithCompletion:^(NSNumber * _Nullable value, NSError * _Nullable readError) { - if (readError) { - CHIP_ERROR readErr = MTRErrorToCHIPErrorCode(readError); - LogNSError("Failed to get current fabric: ", readError); - SetCommandExitStatus(readErr); - return; - } - MTROperationalCredentialsClusterRemoveFabricParams * params = - [[MTROperationalCredentialsClusterRemoveFabricParams alloc] init]; - params.fabricIndex = value; - [opCredsCluster - removeFabricWithParams:params - completion:^(MTROperationalCredentialsClusterNOCResponseParams * _Nullable data, - NSError * _Nullable removeError) { - CHIP_ERROR removeErr = CHIP_NO_ERROR; - if (removeError) { - removeErr = MTRErrorToCHIPErrorCode(removeError); - LogNSError("Failed to remove current fabric: ", removeError); - } else { - ChipLogProgress(chipTool, "Successfully unpaired deviceId %llu", mNodeId); - } - SetCommandExitStatus(removeErr); - }]; - }]; - } - }]; + auto * device = [MTRBaseDevice deviceWithNodeID:@(mNodeId) controller:commissioner]; + + ChipLogProgress(chipTool, "Attempting to unpair device %llu", mNodeId); + MTRBaseClusterOperationalCredentials * opCredsCluster = + [[MTRBaseClusterOperationalCredentials alloc] initWithDevice:device endpointID:@(0) queue:callbackQueue]; + [opCredsCluster readAttributeCurrentFabricIndexWithCompletion:^(NSNumber * _Nullable value, NSError * _Nullable readError) { + if (readError) { + CHIP_ERROR readErr = MTRErrorToCHIPErrorCode(readError); + LogNSError("Failed to get current fabric: ", readError); + SetCommandExitStatus(readErr); + return; + } + MTROperationalCredentialsClusterRemoveFabricParams * params = + [[MTROperationalCredentialsClusterRemoveFabricParams alloc] init]; + params.fabricIndex = value; + [opCredsCluster removeFabricWithParams:params + completion:^(MTROperationalCredentialsClusterNOCResponseParams * _Nullable data, + NSError * _Nullable removeError) { + CHIP_ERROR removeErr = CHIP_NO_ERROR; + if (removeError) { + removeErr = MTRErrorToCHIPErrorCode(removeError); + LogNSError("Failed to remove current fabric: ", removeError); + } else { + ChipLogProgress(chipTool, "Successfully unpaired deviceId %llu", mNodeId); + } + SetCommandExitStatus(removeErr); + }]; + }]; } diff --git a/examples/darwin-framework-tool/commands/pairing/PairingDelegateBridge.mm b/examples/darwin-framework-tool/commands/pairing/PairingDelegateBridge.mm index 1a58f255df30df..5ea335271bce9a 100644 --- a/examples/darwin-framework-tool/commands/pairing/PairingDelegateBridge.mm +++ b/examples/darwin-framework-tool/commands/pairing/PairingDelegateBridge.mm @@ -54,7 +54,7 @@ - (void)onPairingComplete:(NSError *)error ChipLogProgress(chipTool, "Pairing Success"); ChipLogProgress(chipTool, "PASE establishment successful"); NSError * commissionError; - [_commissioner commissionDevice:_deviceID commissioningParams:_params error:&commissionError]; + [_commissioner commissionNodeWithID:@(_deviceID) commissioningParams:_params error:&commissionError]; if (commissionError != nil) { _commandBridge->SetCommandExitStatus(commissionError); return; diff --git a/examples/darwin-framework-tool/commands/tests/TestCommandBridge.h b/examples/darwin-framework-tool/commands/tests/TestCommandBridge.h index 6edd466b9ae681..03f6b70aae719b 100644 --- a/examples/darwin-framework-tool/commands/tests/TestCommandBridge.h +++ b/examples/darwin-framework-tool/commands/tests/TestCommandBridge.h @@ -156,9 +156,10 @@ class TestCommandBridge : public CHIPCommandBridge, SetIdentity(identity); - // Invalidate our existing CASE session; otherwise getConnectedDevice - // will just hand it right back to us without establishing a new CASE - // session when a reboot is done on the server. + // Invalidate our existing CASE session; otherwise trying to work with + // our device will just reuse it without establishing a new CASE + // session when a reboot is done on the server, and then our next + // interaction will time out. if (value.expireExistingSession.ValueOr(true)) { if (GetDevice(identity) != nil) { [GetDevice(identity) invalidateCASESession]; @@ -166,17 +167,10 @@ class TestCommandBridge : public CHIPCommandBridge, } } - [controller getBaseDevice:value.nodeId - queue:mCallbackQueue - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - if (error != nil) { - SetCommandExitStatus(error); - return; - } - - mConnectedDevices[identity] = device; - NextTest(); - }]; + mConnectedDevices[identity] = [MTRBaseDevice deviceWithNodeID:@(value.nodeId) controller:controller]; + dispatch_async(mCallbackQueue, ^{ + NextTest(); + }); return CHIP_NO_ERROR; } @@ -238,7 +232,9 @@ class TestCommandBridge : public CHIPCommandBridge, VerifyOrReturn(commissioner != nil, Exit("No current commissioner")); NSError * commissionError = nil; - [commissioner commissionDevice:nodeId commissioningParams:[[MTRCommissioningParameters alloc] init] error:&commissionError]; + [commissioner commissionNodeWithID:@(nodeId) + commissioningParams:[[MTRCommissioningParameters alloc] init] + error:&commissionError]; CHIP_ERROR err = MTRErrorToCHIPErrorCode(commissionError); if (err != CHIP_NO_ERROR) { Exit("Failed to kick off commissioning", err); diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice.h b/src/darwin/Framework/CHIP/MTRBaseDevice.h index cbd5b1582eca9f..86da7b8be5e70e 100644 --- a/src/darwin/Framework/CHIP/MTRBaseDevice.h +++ b/src/darwin/Framework/CHIP/MTRBaseDevice.h @@ -20,6 +20,7 @@ #import @class MTRSetupPayload; +@class MTRDeviceController; NS_ASSUME_NONNULL_BEGIN @@ -125,6 +126,14 @@ extern NSString * const MTRArrayValueType; - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; +/** + * Create a device object with the given node id and controller. This + * will always succeed, even if there is no such node id on the controller's + * fabric, but attempts to actually use the MTRBaseDevice will fail + * (asynchronously) in that case. + */ ++ (instancetype)deviceWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller MTR_NEWLY_AVAILABLE; + /** * Subscribe to receive attribute reports for everything (all endpoints, all * clusters, all attributes, all events) on the device. diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice.mm b/src/darwin/Framework/CHIP/MTRBaseDevice.mm index e4d56ca71b0b93..25e8908d22b4ae 100644 --- a/src/darwin/Framework/CHIP/MTRBaseDevice.mm +++ b/src/darwin/Framework/CHIP/MTRBaseDevice.mm @@ -231,6 +231,13 @@ - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceControlle return self; } ++ (instancetype)deviceWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller +{ + // Indirect through the controller to give it a chance to create an + // MTRBaseDeviceOverXPC instead of just an MTRBaseDevice. + return [controller baseDeviceForNodeID:nodeID]; +} + - (void)invalidateCASESession { if (self.isPASEDevice) { diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.h b/src/darwin/Framework/CHIP/MTRDeviceController.h index 76f51a18b2b9f7..3ba788bbbfc5f6 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController.h @@ -23,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN +MTR_NEWLY_DEPRECATED("Please use MTRBaseDevice deviceWithNodeID") typedef void (^MTRDeviceConnectionCallback)(MTRBaseDevice * _Nullable device, NSError * _Nullable error); @class MTRCommissioningParameters; @@ -31,6 +32,9 @@ typedef void (^MTRDeviceConnectionCallback)(MTRBaseDevice * _Nullable device, NS @interface MTRDeviceController : NSObject +/** + * If true, the controller has not been shut down yet. + */ @property (readonly, nonatomic, getter=isRunning) BOOL running; /** @@ -73,70 +77,29 @@ typedef void (^MTRDeviceConnectionCallback)(MTRBaseDevice * _Nullable device, NS API_AVAILABLE(ios(16.2), macos(13.1), watchos(9.2), tvos(16.2)); /** - * Start pairing for a device with the given ID, using the provided setup PIN - * to establish a PASE connection. - * - * The IP and port for the device will be discovered automatically based on the - * provided discriminator. - * - * The pairing process will proceed until a PASE session is established or an - * error occurs, then notify onPairingComplete on the MTRDevicePairingDelegate - * for this controller. That delegate is expected to call commissionDevice - * after that point if it wants to commission the device. - */ -- (BOOL)pairDevice:(uint64_t)deviceID - discriminator:(uint16_t)discriminator - setupPINCode:(uint32_t)setupPINCode - error:(NSError * __autoreleasing *)error; - -/** - * Start pairing for a device with the given ID, using the provided IP address - * and port to connect to the device and the provided setup PIN to establish a - * PASE connection. - * - * The pairing process will proceed until a PASE session is established or an - * error occurs, then notify onPairingComplete on the MTRDevicePairingDelegate - * for this controller. That delegate is expected to call commissionDevice - * after that point if it wants to commission the device. - */ -- (BOOL)pairDevice:(uint64_t)deviceID - address:(NSString *)address - port:(uint16_t)port - setupPINCode:(uint32_t)setupPINCode - error:(NSError * __autoreleasing *)error; - -/** - * Start pairing for a device with the given ID and onboarding payload (QR code - * or manual setup code). The payload will be used to discover the device and - * establish a PASE connection. - * - * The pairing process will proceed until a PASE session is established or an - * error occurs, then notify onPairingComplete on the MTRDevicePairingDelegate - * for this controller. That delegate is expected to call commissionDevice - * after that point if it wants to commission the device. + * Commission the node with the given node ID. The node ID must match the node + * ID that was used to set up the commissioning session. */ -- (BOOL)pairDevice:(uint64_t)deviceID onboardingPayload:(NSString *)onboardingPayload error:(NSError * __autoreleasing *)error; -- (BOOL)commissionDevice:(uint64_t)deviceId - commissioningParams:(MTRCommissioningParameters *)commissioningParams - error:(NSError * __autoreleasing *)error; +- (BOOL)commissionNodeWithID:(NSNumber *)nodeID + commissioningParams:(MTRCommissioningParameters *)commissioningParams + error:(NSError * __autoreleasing *)error MTR_NEWLY_AVAILABLE; - (BOOL)continueCommissioningDevice:(void *)device ignoreAttestationFailure:(BOOL)ignoreAttestationFailure error:(NSError * __autoreleasing *)error; -- (BOOL)stopDevicePairing:(uint64_t)deviceID error:(NSError * __autoreleasing *)error; - -- (nullable MTRBaseDevice *)getDeviceBeingCommissioned:(uint64_t)deviceId error:(NSError * __autoreleasing *)error; -- (BOOL)getBaseDevice:(uint64_t)deviceID - queue:(dispatch_queue_t)queue - completion:(MTRDeviceConnectionCallback)completion MTR_NEWLY_AVAILABLE; +/** + * Cancel commissioning for the given node id. This will shut down any existing + * commissioning session for that node id. + */ +- (BOOL)cancelCommissioningForNodeID:(NSNumber *)nodeID error:(NSError * __autoreleasing *)error MTR_NEWLY_AVAILABLE; -- (BOOL)openPairingWindow:(uint64_t)deviceID duration:(NSUInteger)duration error:(NSError * __autoreleasing *)error; -- (nullable NSString *)openPairingWindowWithPIN:(uint64_t)deviceID - duration:(NSUInteger)duration - discriminator:(NSUInteger)discriminator - setupPIN:(NSUInteger)setupPIN - error:(NSError * __autoreleasing *)error; +/** + * Get an MTRBaseDevice for a commissioning session that was set up for the + * given node ID. Returns nil if no such commissioning session is available. + */ +- (nullable MTRBaseDevice *)deviceBeingCommissionedWithNodeID:(NSNumber *)nodeID + error:(NSError * __autoreleasing *)error MTR_NEWLY_AVAILABLE; /** * Controllers are created via the MTRDeviceControllerFactory object. @@ -176,19 +139,24 @@ typedef void (^MTRDeviceConnectionCallback)(MTRBaseDevice * _Nullable device, NS - (NSData * _Nullable)attestationChallengeForDeviceID:(NSNumber *)deviceID MTR_NEWLY_AVAILABLE; /** - * Compute a PASE verifier and passcode ID for the desired setup pincode. + * Compute a PASE verifier for the desired setup passcode. * - * @param[in] setupPincode The desired PIN code to use - * @param[in] iterations The number of iterations to use when generating the verifier - * @param[in] salt The 16-byte salt for verifier computation + * @param[in] setupPasscode The desired passcode to use. + * @param[in] iterations The number of iterations to use when generating the verifier. + * @param[in] salt The 16-byte salt for verifier computation. * * Returns nil on errors (e.g. salt has the wrong size), otherwise the computed * verifier bytes. */ -- (nullable NSData *)computePaseVerifier:(uint32_t)setupPincode iterations:(uint32_t)iterations salt:(NSData *)salt; ++ (nullable NSData *)computePASEVerifierForSetupPasscode:(NSNumber *)setupPasscode + iterations:(NSNumber *)iterations + salt:(NSData *)salt + error:(NSError * __autoreleasing *)error MTR_NEWLY_AVAILABLE; /** - * Shutdown the controller. Calls to shutdown after the first one are NO-OPs. + * Shut down the controller. Calls to shutdown after the first one are NO-OPs. + * This must be called, either directly or via shutting down the + * MTRDeviceControllerFactory, to avoid leaking the controller. */ - (void)shutdown; @@ -206,6 +174,49 @@ typedef void (^MTRDeviceConnectionCallback)(MTRBaseDevice * _Nullable device, NS completionHandler:(MTRDeviceConnectionCallback)completionHandler MTR_NEWLY_DEPRECATED("Please use [MTRBaseDevice deviceWithNodeID:controller:]"); +- (BOOL)pairDevice:(uint64_t)deviceID + discriminator:(uint16_t)discriminator + setupPINCode:(uint32_t)setupPINCode + error:(NSError * __autoreleasing *)error + MTR_NEWLY_DEPRECATED("Please use setupCommissioningSessionWithPayload:newNodeID:error:"); +- (BOOL)pairDevice:(uint64_t)deviceID + address:(NSString *)address + port:(uint16_t)port + setupPINCode:(uint32_t)setupPINCode + error:(NSError * __autoreleasing *)error + MTR_NEWLY_DEPRECATED("Please use setupCommissioningSessionWithPayload:newNodeID:error:"); +- (BOOL)pairDevice:(uint64_t)deviceID + onboardingPayload:(NSString *)onboardingPayload + error:(NSError * __autoreleasing *)error + MTR_NEWLY_DEPRECATED("Please use setupCommissioningSessionWithPayload:newNodeID:error:"); +- (BOOL)commissionDevice:(uint64_t)deviceId + commissioningParams:(MTRCommissioningParameters *)commissioningParams + error:(NSError * __autoreleasing *)error + MTR_NEWLY_DEPRECATED("Please use commissionNodeWithID:commissioningParams:error:"); + +- (BOOL)stopDevicePairing:(uint64_t)deviceID + error:(NSError * __autoreleasing *)error MTR_NEWLY_DEPRECATED("Please use cancelCommissioningForNodeID:error:"); + +- (nullable MTRBaseDevice *)getDeviceBeingCommissioned:(uint64_t)deviceId + error:(NSError * __autoreleasing *)error + MTR_NEWLY_DEPRECATED("Please use deviceBeingCommissionedWithNodeID:error:"); + +- (BOOL)openPairingWindow:(uint64_t)deviceID + duration:(NSUInteger)duration + error:(NSError * __autoreleasing *)error + MTR_NEWLY_DEPRECATED("Please use MTRDevice or MTRBaseDevice openCommissioningWindowWithSetupPasscode"); +- (nullable NSString *)openPairingWindowWithPIN:(uint64_t)deviceID + duration:(NSUInteger)duration + discriminator:(NSUInteger)discriminator + setupPIN:(NSUInteger)setupPIN + error:(NSError * __autoreleasing *)error + MTR_NEWLY_DEPRECATED("Please use MTRDevice or MTRBaseDevice openCommissioningWindowWithSetupPasscode"); + +- (nullable NSData *)computePaseVerifier:(uint32_t)setupPincode + iterations:(uint32_t)iterations + salt:(NSData *)salt + MTR_NEWLY_DEPRECATED("Please use computePASEVerifierForSetupPasscode:iterations:salt:error:"); + @end NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index 07e77b8b34398c..08e98db7af284a 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -74,6 +74,8 @@ static NSString * const kErrorCSRValidation = @"Extracting public key from CSR failed"; static NSString * const kErrorGetCommissionee = @"Failure obtaining device being commissioned"; static NSString * const kErrorGetAttestationChallenge = @"Failure getting attestation challenge"; +static NSString * const kErrorSpake2pVerifierGenerationFailed = @"PASE verifier generation failed"; +static NSString * const kErrorSpake2pVerifierSerializationFailed = @"PASE verifier serialization failed"; @interface MTRDeviceController () @@ -379,91 +381,22 @@ - (BOOL)setupCommissioningSessionWithPayload:(MTRSetupPayload *)payload pairingCode = [payload manualEntryCode]; } if (pairingCode == nil) { - success = ![self checkForError:CHIP_ERROR_INVALID_ARGUMENT logMsg:kErrorSetupCodeGen error:error]; + success = ![MTRDeviceController checkForError:CHIP_ERROR_INVALID_ARGUMENT logMsg:kErrorSetupCodeGen error:error]; return; } chip::NodeId nodeId = [newNodeID unsignedLongLongValue]; _operationalCredentialsDelegate->SetDeviceID(nodeId); CHIP_ERROR errorCode = self.cppCommissioner->EstablishPASEConnection(nodeId, [pairingCode UTF8String]); - success = ![self checkForError:errorCode logMsg:kErrorPairDevice error:error]; + success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorPairDevice error:error]; }); return success; } -- (BOOL)pairDevice:(uint64_t)deviceID - discriminator:(uint16_t)discriminator - setupPINCode:(uint32_t)setupPINCode - error:(NSError * __autoreleasing *)error -{ - VerifyOrReturnValue([self checkIsRunning:error], NO); - - __block BOOL success = NO; - dispatch_sync(_chipWorkQueue, ^{ - VerifyOrReturn([self checkIsRunning:error]); - - std::string manualPairingCode; - chip::SetupPayload payload; - payload.discriminator.SetLongValue(discriminator); - payload.setUpPINCode = setupPINCode; - - auto errorCode = chip::ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manualPairingCode); - success = ![self checkForError:errorCode logMsg:kErrorSetupCodeGen error:error]; - VerifyOrReturn(success); - - _operationalCredentialsDelegate->SetDeviceID(deviceID); - errorCode = self.cppCommissioner->EstablishPASEConnection(deviceID, manualPairingCode.c_str()); - success = ![self checkForError:errorCode logMsg:kErrorPairDevice error:error]; - }); - - return success; -} - -- (BOOL)pairDevice:(uint64_t)deviceID - address:(NSString *)address - port:(uint16_t)port - setupPINCode:(uint32_t)setupPINCode - error:(NSError * __autoreleasing *)error -{ - VerifyOrReturnValue([self checkIsRunning:error], NO); - - __block BOOL success = NO; - dispatch_sync(_chipWorkQueue, ^{ - VerifyOrReturn([self checkIsRunning:error]); - - chip::Inet::IPAddress addr; - chip::Inet::IPAddress::FromString([address UTF8String], addr); - chip::Transport::PeerAddress peerAddress = chip::Transport::PeerAddress::UDP(addr, port); - - _operationalCredentialsDelegate->SetDeviceID(deviceID); - - auto params = chip::RendezvousParameters().SetSetupPINCode(setupPINCode).SetPeerAddress(peerAddress); - auto errorCode = self.cppCommissioner->EstablishPASEConnection(deviceID, params); - success = ![self checkForError:errorCode logMsg:kErrorPairDevice error:error]; - }); - - return success; -} - -- (BOOL)pairDevice:(uint64_t)deviceID onboardingPayload:(NSString *)onboardingPayload error:(NSError * __autoreleasing *)error -{ - VerifyOrReturnValue([self checkIsRunning:error], NO); - - __block BOOL success = NO; - dispatch_sync(_chipWorkQueue, ^{ - VerifyOrReturn([self checkIsRunning:error]); - - _operationalCredentialsDelegate->SetDeviceID(deviceID); - auto errorCode = self.cppCommissioner->EstablishPASEConnection(deviceID, [onboardingPayload UTF8String]); - success = ![self checkForError:errorCode logMsg:kErrorPairDevice error:error]; - }); - return success; -} - -- (BOOL)commissionDevice:(uint64_t)deviceID - commissioningParams:(MTRCommissioningParameters *)commissioningParams - error:(NSError * __autoreleasing *)error +- (BOOL)commissionNodeWithID:(NSNumber *)nodeID + commissioningParams:(MTRCommissioningParameters *)commissioningParams + error:(NSError * __autoreleasing *)error { VerifyOrReturnValue([self checkIsRunning:error], NO); @@ -508,9 +441,10 @@ - (BOOL)commissionDevice:(uint64_t)deviceID params.SetDeviceAttestationDelegate(_deviceAttestationDelegateBridge); } - _operationalCredentialsDelegate->SetDeviceID(deviceID); - auto errorCode = self.cppCommissioner->Commission(deviceID, params); - success = ![self checkForError:errorCode logMsg:kErrorPairDevice error:error]; + chip::NodeId deviceId = [nodeID unsignedLongLongValue]; + _operationalCredentialsDelegate->SetDeviceID(deviceId); + auto errorCode = self.cppCommissioner->Commission(deviceId, params); + success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorPairDevice error:error]; }); return success; } @@ -532,12 +466,12 @@ - (BOOL)continueCommissioningDevice:(void *)device auto deviceProxy = static_cast(device); auto errorCode = self.cppCommissioner->ContinueCommissioningAfterDeviceAttestation(deviceProxy, ignoreAttestationFailure ? chip::Credentials::AttestationVerificationResult::kSuccess : lastAttestationResult); - success = ![self checkForError:errorCode logMsg:kErrorPairDevice error:error]; + success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorPairDevice error:error]; }); return success; } -- (BOOL)stopDevicePairing:(uint64_t)deviceID error:(NSError * __autoreleasing *)error +- (BOOL)cancelCommissioningForNodeID:(NSNumber *)nodeID error:(NSError * __autoreleasing *)error { VerifyOrReturnValue([self checkIsRunning:error], NO); @@ -546,14 +480,13 @@ - (BOOL)stopDevicePairing:(uint64_t)deviceID error:(NSError * __autoreleasing *) VerifyOrReturn([self checkIsRunning:error]); _operationalCredentialsDelegate->ResetDeviceID(); - auto errorCode = self.cppCommissioner->StopPairing(deviceID); - success = ![self checkForError:errorCode logMsg:kErrorStopPairing error:error]; + auto errorCode = self.cppCommissioner->StopPairing([nodeID unsignedLongLongValue]); + success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorStopPairing error:error]; }); - return success; } -- (MTRBaseDevice *)getDeviceBeingCommissioned:(uint64_t)deviceId error:(NSError * __autoreleasing *)error +- (MTRBaseDevice *)deviceBeingCommissionedWithNodeID:(NSNumber *)nodeID error:(NSError * __autoreleasing *)error { VerifyOrReturnValue([self checkIsRunning:error], nil); @@ -563,8 +496,8 @@ - (MTRBaseDevice *)getDeviceBeingCommissioned:(uint64_t)deviceId error:(NSError VerifyOrReturn([self checkIsRunning:error]); chip::CommissioneeDeviceProxy * deviceProxy; - auto errorCode = self->_cppCommissioner->GetDeviceBeingCommissioned(deviceId, &deviceProxy); - success = ![self checkForError:errorCode logMsg:kErrorStopPairing error:error]; + auto errorCode = self->_cppCommissioner->GetDeviceBeingCommissioned(nodeID.unsignedLongLongValue, &deviceProxy); + success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorStopPairing error:error]; device = [[MTRBaseDevice alloc] initWithPASEDevice:deviceProxy controller:self]; }); VerifyOrReturnValue(success, nil); @@ -572,10 +505,9 @@ - (MTRBaseDevice *)getDeviceBeingCommissioned:(uint64_t)deviceId error:(NSError return device; } -- (BOOL)getBaseDevice:(uint64_t)deviceID queue:(dispatch_queue_t)queue completion:(MTRDeviceConnectionCallback)completion +- (MTRBaseDevice *)baseDeviceForNodeID:(NSNumber *)nodeID { - // Will get removed once deviceWithNodeID exists. - return [self getBaseDevice:deviceID queue:queue completionHandler:completion]; + return [[MTRBaseDevice alloc] initWithNodeID:nodeID controller:self]; } - (MTRDevice *)deviceForNodeID:(NSNumber *)nodeID @@ -603,90 +535,6 @@ - (void)removeDevice:(MTRDevice *)device os_unfair_lock_unlock(&_deviceMapLock); } -- (BOOL)openPairingWindow:(uint64_t)deviceID duration:(NSUInteger)duration error:(NSError * __autoreleasing *)error -{ - VerifyOrReturnValue([self checkIsRunning:error], NO); - - if (duration > UINT16_MAX) { - MTR_LOG_ERROR("Error: Duration %tu is too large. Max value %d", duration, UINT16_MAX); - if (error) { - *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]; - } - return NO; - } - - __block BOOL success = NO; - dispatch_sync(_chipWorkQueue, ^{ - VerifyOrReturn([self checkIsRunning:error]); - - auto errorCode = chip::Controller::AutoCommissioningWindowOpener::OpenBasicCommissioningWindow( - self.cppCommissioner, deviceID, chip::System::Clock::Seconds16(static_cast(duration))); - success = ![self checkForError:errorCode logMsg:kErrorOpenPairingWindow error:error]; - }); - - return success; -} - -- (NSString *)openPairingWindowWithPIN:(uint64_t)deviceID - duration:(NSUInteger)duration - discriminator:(NSUInteger)discriminator - setupPIN:(NSUInteger)setupPIN - error:(NSError * __autoreleasing *)error -{ - __block NSString * rv = nil; - - VerifyOrReturnValue([self checkIsRunning:error], rv); - - if (duration > UINT16_MAX) { - MTR_LOG_ERROR("Error: Duration %tu is too large. Max value %d", duration, UINT16_MAX); - if (error) { - *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]; - } - return rv; - } - - if (discriminator > 0xfff) { - MTR_LOG_ERROR("Error: Discriminator %tu is too large. Max value %d", discriminator, 0xfff); - if (error) { - *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]; - } - return rv; - } - - if (!chip::CanCastTo(setupPIN) || !chip::SetupPayload::IsValidSetupPIN(static_cast(setupPIN))) { - MTR_LOG_ERROR("Error: Setup pin %lu is not valid", static_cast(setupPIN)); - if (error) { - *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]; - } - return rv; - } - - dispatch_sync(_chipWorkQueue, ^{ - VerifyOrReturn([self checkIsRunning:error]); - - chip::SetupPayload setupPayload; - auto errorCode = chip::Controller::AutoCommissioningWindowOpener::OpenCommissioningWindow(self.cppCommissioner, deviceID, - chip::System::Clock::Seconds16(static_cast(duration)), chip::Crypto::kSpake2p_Min_PBKDF_Iterations, - static_cast(discriminator), chip::MakeOptional(static_cast(setupPIN)), chip::NullOptional, - setupPayload); - - auto success = ![self checkForError:errorCode logMsg:kErrorOpenPairingWindow error:error]; - VerifyOrReturn(success); - - chip::ManualSetupPayloadGenerator generator(setupPayload); - std::string outCode; - - if (generator.payloadDecimalStringRepresentation(outCode) == CHIP_NO_ERROR) { - MTR_LOG_ERROR("Setup code is %s", outCode.c_str()); - rv = [NSString stringWithCString:outCode.c_str() encoding:[NSString defaultCStringEncoding]]; - } else { - MTR_LOG_ERROR("Failed to get decimal setup code"); - } - }); - - return rv; -} - - (void)setPairingDelegate:(id)delegate queue:(dispatch_queue_t)queue { VerifyOrReturn([self checkIsRunning]); @@ -714,20 +562,23 @@ - (void)setNocChainIssuer:(id)nocChainIssuer queue:(dispatch_ }); } -- (nullable NSData *)computePaseVerifier:(uint32_t)setupPincode iterations:(uint32_t)iterations salt:(NSData *)salt ++ (nullable NSData *)computePASEVerifierForSetupPasscode:(NSNumber *)setupPasscode + iterations:(NSNumber *)iterations + salt:(NSData *)salt + error:(NSError * __autoreleasing *)error { + // Spake2pVerifier::Generate takes the passcode by non-const reference for some reason. + uint32_t unboxedSetupPasscode = [setupPasscode unsignedIntValue]; chip::Spake2pVerifier verifier; - CHIP_ERROR err = verifier.Generate(iterations, AsByteSpan(salt), setupPincode); - if (err != CHIP_NO_ERROR) { - MTR_LOG_ERROR("computePaseVerifier generation failed: %s", chip::ErrorStr(err)); + CHIP_ERROR err = verifier.Generate([iterations unsignedIntValue], AsByteSpan(salt), unboxedSetupPasscode); + if ([MTRDeviceController checkForError:err logMsg:kErrorSpake2pVerifierGenerationFailed error:error]) { return nil; } uint8_t serializedBuffer[chip::Crypto::kSpake2p_VerifierSerialized_Length]; chip::MutableByteSpan serializedBytes(serializedBuffer); err = verifier.Serialize(serializedBytes); - if (err != CHIP_NO_ERROR) { - MTR_LOG_ERROR("computePaseVerifier serialization failed: %s", chip::ErrorStr(err)); + if ([MTRDeviceController checkForError:err logMsg:kErrorSpake2pVerifierSerializationFailed error:error]) { return nil; } @@ -744,14 +595,14 @@ - (NSData * _Nullable)attestationChallengeForDeviceID:(NSNumber *)deviceID chip::CommissioneeDeviceProxy * deviceProxy; auto errorCode = self.cppCommissioner->GetDeviceBeingCommissioned([deviceID unsignedLongLongValue], &deviceProxy); - auto success = ![self checkForError:errorCode logMsg:kErrorGetCommissionee error:nil]; + auto success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorGetCommissionee error:nil]; VerifyOrReturn(success); uint8_t challengeBuffer[chip::Crypto::kAES_CCM128_Key_Length]; chip::ByteSpan challenge(challengeBuffer); errorCode = deviceProxy->GetAttestationChallenge(challenge); - success = ![self checkForError:errorCode logMsg:kErrorGetAttestationChallenge error:nil]; + success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorGetAttestationChallenge error:nil]; VerifyOrReturn(success); attestationChallenge = AsData(challenge); @@ -792,7 +643,7 @@ - (BOOL)checkForStartError:(CHIP_ERROR)errorCode logMsg:(NSString *)logMsg return YES; } -- (BOOL)checkForError:(CHIP_ERROR)errorCode logMsg:(NSString *)logMsg error:(NSError * __autoreleasing *)error ++ (BOOL)checkForError:(CHIP_ERROR)errorCode logMsg:(NSString *)logMsg error:(NSError * __autoreleasing *)error { if (CHIP_NO_ERROR == errorCode) { return NO; @@ -1016,4 +867,179 @@ - (BOOL)getBaseDevice:(uint64_t)deviceID queue:(dispatch_queue_t)queue completio }]; } +- (BOOL)pairDevice:(uint64_t)deviceID + discriminator:(uint16_t)discriminator + setupPINCode:(uint32_t)setupPINCode + error:(NSError * __autoreleasing *)error +{ + VerifyOrReturnValue([self checkIsRunning:error], NO); + + __block BOOL success = NO; + dispatch_sync(_chipWorkQueue, ^{ + VerifyOrReturn([self checkIsRunning:error]); + + std::string manualPairingCode; + chip::SetupPayload payload; + payload.discriminator.SetLongValue(discriminator); + payload.setUpPINCode = setupPINCode; + + auto errorCode = chip::ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manualPairingCode); + success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorSetupCodeGen error:error]; + VerifyOrReturn(success); + + _operationalCredentialsDelegate->SetDeviceID(deviceID); + errorCode = self.cppCommissioner->EstablishPASEConnection(deviceID, manualPairingCode.c_str()); + success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorPairDevice error:error]; + }); + + return success; +} + +- (BOOL)pairDevice:(uint64_t)deviceID + address:(NSString *)address + port:(uint16_t)port + setupPINCode:(uint32_t)setupPINCode + error:(NSError * __autoreleasing *)error +{ + VerifyOrReturnValue([self checkIsRunning:error], NO); + + __block BOOL success = NO; + dispatch_sync(_chipWorkQueue, ^{ + VerifyOrReturn([self checkIsRunning:error]); + + chip::Inet::IPAddress addr; + chip::Inet::IPAddress::FromString([address UTF8String], addr); + chip::Transport::PeerAddress peerAddress = chip::Transport::PeerAddress::UDP(addr, port); + + _operationalCredentialsDelegate->SetDeviceID(deviceID); + + auto params = chip::RendezvousParameters().SetSetupPINCode(setupPINCode).SetPeerAddress(peerAddress); + auto errorCode = self.cppCommissioner->EstablishPASEConnection(deviceID, params); + success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorPairDevice error:error]; + }); + + return success; +} + +- (BOOL)pairDevice:(uint64_t)deviceID onboardingPayload:(NSString *)onboardingPayload error:(NSError * __autoreleasing *)error +{ + VerifyOrReturnValue([self checkIsRunning:error], NO); + + __block BOOL success = NO; + dispatch_sync(_chipWorkQueue, ^{ + VerifyOrReturn([self checkIsRunning:error]); + + _operationalCredentialsDelegate->SetDeviceID(deviceID); + auto errorCode = self.cppCommissioner->EstablishPASEConnection(deviceID, [onboardingPayload UTF8String]); + success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorPairDevice error:error]; + }); + return success; +} + +- (BOOL)commissionDevice:(uint64_t)deviceID + commissioningParams:(MTRCommissioningParameters *)commissioningParams + error:(NSError * __autoreleasing *)error +{ + return [self commissionNodeWithID:@(deviceID) commissioningParams:commissioningParams error:error]; +} + +- (BOOL)stopDevicePairing:(uint64_t)deviceID error:(NSError * __autoreleasing *)error +{ + return [self cancelCommissioningForNodeID:@(deviceID) error:error]; +} + +- (MTRBaseDevice *)getDeviceBeingCommissioned:(uint64_t)deviceId error:(NSError * __autoreleasing *)error +{ + return [self deviceBeingCommissionedWithNodeID:@(deviceId) error:error]; +} + +- (BOOL)openPairingWindow:(uint64_t)deviceID duration:(NSUInteger)duration error:(NSError * __autoreleasing *)error +{ + VerifyOrReturnValue([self checkIsRunning:error], NO); + + if (duration > UINT16_MAX) { + MTR_LOG_ERROR("Error: Duration %tu is too large. Max value %d", duration, UINT16_MAX); + if (error) { + *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]; + } + return NO; + } + + __block BOOL success = NO; + dispatch_sync(_chipWorkQueue, ^{ + VerifyOrReturn([self checkIsRunning:error]); + + auto errorCode = chip::Controller::AutoCommissioningWindowOpener::OpenBasicCommissioningWindow( + self.cppCommissioner, deviceID, chip::System::Clock::Seconds16(static_cast(duration))); + success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorOpenPairingWindow error:error]; + }); + + return success; +} + +- (NSString *)openPairingWindowWithPIN:(uint64_t)deviceID + duration:(NSUInteger)duration + discriminator:(NSUInteger)discriminator + setupPIN:(NSUInteger)setupPIN + error:(NSError * __autoreleasing *)error +{ + __block NSString * rv = nil; + + VerifyOrReturnValue([self checkIsRunning:error], rv); + + if (duration > UINT16_MAX) { + MTR_LOG_ERROR("Error: Duration %tu is too large. Max value %d", duration, UINT16_MAX); + if (error) { + *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]; + } + return rv; + } + + if (discriminator > 0xfff) { + MTR_LOG_ERROR("Error: Discriminator %tu is too large. Max value %d", discriminator, 0xfff); + if (error) { + *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]; + } + return rv; + } + + if (!chip::CanCastTo(setupPIN) || !chip::SetupPayload::IsValidSetupPIN(static_cast(setupPIN))) { + MTR_LOG_ERROR("Error: Setup pin %lu is not valid", static_cast(setupPIN)); + if (error) { + *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]; + } + return rv; + } + + dispatch_sync(_chipWorkQueue, ^{ + VerifyOrReturn([self checkIsRunning:error]); + + chip::SetupPayload setupPayload; + auto errorCode = chip::Controller::AutoCommissioningWindowOpener::OpenCommissioningWindow(self.cppCommissioner, deviceID, + chip::System::Clock::Seconds16(static_cast(duration)), chip::Crypto::kSpake2p_Min_PBKDF_Iterations, + static_cast(discriminator), chip::MakeOptional(static_cast(setupPIN)), chip::NullOptional, + setupPayload); + + auto success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorOpenPairingWindow error:error]; + VerifyOrReturn(success); + + chip::ManualSetupPayloadGenerator generator(setupPayload); + std::string outCode; + + if (generator.payloadDecimalStringRepresentation(outCode) == CHIP_NO_ERROR) { + MTR_LOG_ERROR("Setup code is %s", outCode.c_str()); + rv = [NSString stringWithCString:outCode.c_str() encoding:[NSString defaultCStringEncoding]]; + } else { + MTR_LOG_ERROR("Failed to get decimal setup code"); + } + }); + + return rv; +} + +- (nullable NSData *)computePaseVerifier:(uint32_t)setupPincode iterations:(uint32_t)iterations salt:(NSData *)salt +{ + return [MTRDeviceController computePASEVerifierForSetupPasscode:@(setupPincode) iterations:@(iterations) salt:salt error:nil]; +} + @end diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerOverXPC.m b/src/darwin/Framework/CHIP/MTRDeviceControllerOverXPC.m index fcca97a9234b0e..f8f2e1827185e6 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerOverXPC.m +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerOverXPC.m @@ -63,6 +63,9 @@ - (BOOL)pairDevice:(uint64_t)deviceID error:(NSError * __autoreleasing *)error { MTR_LOG_ERROR("MTRDevice doesn't support pairDevice over XPC"); + if (error != nil) { + *error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil]; + } return NO; } @@ -74,12 +77,18 @@ - (BOOL)pairDevice:(uint64_t)deviceID error:(NSError * __autoreleasing *)error { MTR_LOG_ERROR("MTRDevice doesn't support pairDevice over XPC"); + if (error != nil) { + *error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil]; + } return NO; } - (BOOL)pairDevice:(uint64_t)deviceID onboardingPayload:(NSString *)onboardingPayload error:(NSError * __autoreleasing *)error { MTR_LOG_ERROR("MTRDevice doesn't support pairDevice over XPC"); + if (error != nil) { + *error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil]; + } return NO; } @@ -87,37 +96,88 @@ - (BOOL)commissionDevice:(uint64_t)deviceID commissioningParams:(MTRCommissioningParameters *)commissioningParams error:(NSError * __autoreleasing *)error { - MTR_LOG_ERROR("MTRDevice doesn't support pairDevice over XPC"); + MTR_LOG_ERROR("MTRDevice doesn't support commissionDevice over XPC"); + if (error != nil) { + *error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil]; + } return NO; } -- (void)setListenPort:(uint16_t)port -{ - MTR_LOG_ERROR("MTRDevice doesn't support setListenPort over XPC"); -} - - (BOOL)stopDevicePairing:(uint64_t)deviceID error:(NSError * __autoreleasing *)error { MTR_LOG_ERROR("MTRDevice doesn't support stopDevicePairing over XPC"); + if (error != nil) { + *error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil]; + } return NO; } - (nullable MTRBaseDevice *)getDeviceBeingCommissioned:(uint64_t)deviceID error:(NSError * __autoreleasing *)error { MTR_LOG_ERROR("MTRDevice doesn't support getDeviceBeingCommissioned over XPC"); + if (error != nil) { + *error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil]; + } + return nil; +} + +- (BOOL)commissionNodeWithID:(NSNumber *)nodeID + commissioningParams:(MTRCommissioningParameters *)commissioningParams + error:(NSError * __autoreleasing *)error; +{ + MTR_LOG_ERROR("MTRDeviceController doesn't support commissionNodeWithID over XPC"); + if (error != nil) { + *error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil]; + } + return NO; +} + +- (BOOL)cancelCommissioningForNodeID:(NSNumber *)nodeID error:(NSError * __autoreleasing *)error +{ + MTR_LOG_ERROR("MTRDeviceController doesn't support cancelCommissioningForNodeID over XPC"); + if (error != nil) { + *error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil]; + } + return NO; +} + +- (nullable MTRBaseDevice *)deviceBeingCommissionedWithNodeID:(NSNumber *)nodeID error:(NSError * __autoreleasing *)error +{ + MTR_LOG_ERROR("MTRDeviceController doesn't support deviceBeingCommissionedWithNodeID over XPC"); + if (error != nil) { + *error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidState userInfo:nil]; + } return nil; } - (BOOL)getBaseDevice:(uint64_t)deviceID queue:(dispatch_queue_t)queue completionHandler:(MTRDeviceConnectionCallback)completionHandler +{ + // Consumers expect their getAnyDeviceControllerWithCompletion to be called + // under here if we don't have a controller id aleady, so make sure we do + // that. + [self fetchControllerIdWithQueue:queue + completion:^(id _Nullable controllerID, NSError * _Nullable error) { + if (error != nil) { + completionHandler(nil, error); + return; + } + + __auto_type * device = [self baseDeviceForNodeID:@(deviceID)]; + completionHandler(device, nil); + }]; + return YES; +} + +- (void)fetchControllerIdWithQueue:(dispatch_queue_t)queue completion:(MTRFetchControllerIDCompletion)completion { dispatch_async(_workQueue, ^{ dispatch_group_t group = dispatch_group_create(); if (!self.controllerID) { dispatch_group_enter(group); [self.xpcConnection getProxyHandleWithCompletion:^( - dispatch_queue_t _Nonnull queue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { + dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { if (handle) { [handle.proxy getAnyDeviceControllerWithCompletion:^(id _Nullable controller, NSError * _Nullable error) { if (error) { @@ -137,16 +197,17 @@ - (BOOL)getBaseDevice:(uint64_t)deviceID } dispatch_group_notify(group, queue, ^{ if (self.controllerID) { - MTRDeviceOverXPC * device = [[MTRDeviceOverXPC alloc] initWithController:self.controllerID - deviceID:@(deviceID) - xpcConnection:self.xpcConnection]; - completionHandler(device, nil); + completion(self.controllerID, nil); } else { - completionHandler(nil, [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil]); + completion(nil, [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil]); } }); }); - return YES; +} + +- (MTRBaseDevice *)baseDeviceForNodeID:(NSNumber *)nodeID +{ + return [[MTRDeviceOverXPC alloc] initWithControllerOverXPC:self deviceID:nodeID xpcConnection:self.xpcConnection]; } - (BOOL)openPairingWindow:(uint64_t)deviceID duration:(NSUInteger)duration error:(NSError * __autoreleasing *)error diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerOverXPC_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceControllerOverXPC_Internal.h index 43d236e10b975e..d772dab879f077 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerOverXPC_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerOverXPC_Internal.h @@ -20,12 +20,17 @@ NS_ASSUME_NONNULL_BEGIN +typedef void (^MTRFetchControllerIDCompletion)(id _Nullable controllerID, NSError * _Nullable error); + @interface MTRDeviceControllerOverXPC () @property (nonatomic, readwrite, strong) id _Nullable controllerID; @property (nonatomic, readonly, strong) dispatch_queue_t workQueue; @property (nonatomic, readonly, strong) MTRDeviceControllerXPCConnection * xpcConnection; +// Guarantees that completion is called with either a non-nil controllerID or a +// non-nil error. +- (void)fetchControllerIdWithQueue:(dispatch_queue_t)queue completion:(MTRFetchControllerIDCompletion)completion; @end NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h index d787aa02c329b1..bed9b70162fec8 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h @@ -154,6 +154,13 @@ NS_ASSUME_NONNULL_BEGIN - (void)asyncDispatchToMatterQueue:(void (^)(chip::Controller::DeviceCommissioner *))block errorHandler:(void (^)(NSError *))errorHandler; +/** + * Get an MTRBaseDevice for the given node id. This exists to allow subclasses + * of MTRDeviceController (e.g. MTRDeviceControllerOverXPC) to override what + * sort of MTRBaseDevice they return. + */ +- (MTRBaseDevice *)baseDeviceForNodeID:(NSNumber *)nodeID; + #pragma mark - Device-specific data and SDK access // DeviceController will act as a central repository for this opaque dictionary that MTRDevice manages - (MTRDevice *)deviceForNodeID:(NSNumber *)nodeID; diff --git a/src/darwin/Framework/CHIP/MTRDeviceOverXPC.h b/src/darwin/Framework/CHIP/MTRDeviceOverXPC.h index 2f7915014e2782..9718c3ee34a3f7 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceOverXPC.h +++ b/src/darwin/Framework/CHIP/MTRDeviceOverXPC.h @@ -19,6 +19,8 @@ #import "MTRCluster.h" // For MTRSubscriptionEstablishedHandler #import "MTRDeviceControllerXPCConnection.h" +@class MTRDeviceControllerOverXPC; + NS_ASSUME_NONNULL_BEGIN @interface MTRDeviceOverXPC : MTRBaseDevice @@ -26,9 +28,9 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; -- (instancetype)initWithController:(id)controller - deviceID:(NSNumber *)deviceID - xpcConnection:(MTRDeviceControllerXPCConnection *)xpcConnection; +- (instancetype)initWithControllerOverXPC:(MTRDeviceControllerOverXPC *)controllerOverXPC + deviceID:(NSNumber *)deviceID + xpcConnection:(MTRDeviceControllerXPCConnection *)xpcConnection; @end diff --git a/src/darwin/Framework/CHIP/MTRDeviceOverXPC.m b/src/darwin/Framework/CHIP/MTRDeviceOverXPC.m index 9a3663fad87106..921eef662fbaed 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceOverXPC.m +++ b/src/darwin/Framework/CHIP/MTRDeviceOverXPC.m @@ -20,6 +20,7 @@ #import "MTRAttributeCacheContainer+XPC.h" #import "MTRCluster.h" #import "MTRDeviceController+XPC.h" +#import "MTRDeviceControllerOverXPC_Internal.h" #import "MTRDeviceControllerXPCConnection.h" #import "MTRError.h" #import "MTRLogging.h" @@ -28,7 +29,8 @@ @interface MTRDeviceOverXPC () -@property (nonatomic, strong, readonly) id controller; +@property (nonatomic, strong, readonly, nullable) id controllerID; +@property (nonatomic, strong, readonly) MTRDeviceControllerOverXPC * controller; @property (nonatomic, readonly) NSNumber * nodeID; @property (nonatomic, strong, readonly) MTRDeviceControllerXPCConnection * xpcConnection; @@ -36,11 +38,12 @@ @interface MTRDeviceOverXPC () @implementation MTRDeviceOverXPC -- (instancetype)initWithController:(id)controller - deviceID:(NSNumber *)deviceID - xpcConnection:(MTRDeviceControllerXPCConnection *)xpcConnection +- (instancetype)initWithControllerOverXPC:(MTRDeviceControllerOverXPC *)controllerOverXPC + deviceID:(NSNumber *)deviceID + xpcConnection:(MTRDeviceControllerXPCConnection *)xpcConnection { - _controller = controller; + _controllerID = controllerOverXPC.controllerID; + _controller = controllerOverXPC; _nodeID = deviceID; _xpcConnection = xpcConnection; return self; @@ -57,13 +60,14 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue { MTR_LOG_DEBUG("Subscribing all attributes... Note that attributeReportHandler, eventReportHandler, and resubscriptionScheduled " "are not supported."); - if (attributeCacheContainer) { - [attributeCacheContainer setXPCConnection:_xpcConnection controllerID:self.controller deviceID:self.nodeID]; - } - [_xpcConnection - getProxyHandleWithCompletion:^(dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { + __auto_type workBlock = ^{ + if (attributeCacheContainer) { + [attributeCacheContainer setXPCConnection:self->_xpcConnection controllerID:self.controllerID deviceID:self.nodeID]; + } + [self->_xpcConnection getProxyHandleWithCompletion:^( + dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { if (handle) { - [handle.proxy subscribeWithController:self.controller + [handle.proxy subscribeWithController:self.controllerID nodeId:self.nodeID.unsignedLongLongValue minInterval:params.minInterval maxInterval:params.maxInterval @@ -87,6 +91,23 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue }); } }]; + }; + + if (self.controllerID != nil) { + workBlock(); + } else { + [self.controller fetchControllerIdWithQueue:queue + completion:^(id _Nullable controllerID, NSError * _Nullable error) { + if (error != nil) { + // We're already running on the right queue. + errorHandler(error); + return; + } + + self->_controllerID = controllerID; + workBlock(); + }]; + } } - (void)readAttributesWithEndpointID:(NSNumber * _Nullable)endpointID @@ -97,10 +118,11 @@ - (void)readAttributesWithEndpointID:(NSNumber * _Nullable)endpointID completion:(MTRDeviceResponseHandler)completion { MTR_LOG_DEBUG("Reading attribute ..."); - [_xpcConnection - getProxyHandleWithCompletion:^(dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { + __auto_type workBlock = ^{ + [self->_xpcConnection getProxyHandleWithCompletion:^( + dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { if (handle) { - [handle.proxy readAttributeWithController:self.controller + [handle.proxy readAttributeWithController:self.controllerID nodeId:self.nodeID.unsignedLongLongValue endpointId:endpointID clusterId:clusterID @@ -123,6 +145,23 @@ - (void)readAttributesWithEndpointID:(NSNumber * _Nullable)endpointID }); } }]; + }; + + if (self.controllerID != nil) { + workBlock(); + } else { + [self.controller fetchControllerIdWithQueue:queue + completion:^(id _Nullable controllerID, NSError * _Nullable error) { + if (error != nil) { + // We're already running on the right queue. + completion(nil, error); + return; + } + + self->_controllerID = controllerID; + workBlock(); + }]; + } } - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID @@ -134,10 +173,11 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID completion:(MTRDeviceResponseHandler)completion { MTR_LOG_DEBUG("Writing attribute ..."); - [_xpcConnection - getProxyHandleWithCompletion:^(dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { + __auto_type workBlock = ^{ + [self->_xpcConnection getProxyHandleWithCompletion:^( + dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { if (handle) { - [handle.proxy writeAttributeWithController:self.controller + [handle.proxy writeAttributeWithController:self.controllerID nodeId:self.nodeID.unsignedLongLongValue endpointId:endpointID clusterId:clusterID @@ -161,6 +201,23 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID }); } }]; + }; + + if (self.controllerID != nil) { + workBlock(); + } else { + [self.controller fetchControllerIdWithQueue:queue + completion:^(id _Nullable controllerID, NSError * _Nullable error) { + if (error != nil) { + // We're already running on the right queue. + completion(nil, error); + return; + } + + self->_controllerID = controllerID; + workBlock(); + }]; + } } - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID @@ -172,10 +229,11 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID completion:(MTRDeviceResponseHandler)completion { MTR_LOG_DEBUG("Invoking command ..."); - [_xpcConnection - getProxyHandleWithCompletion:^(dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { + __auto_type workBlock = ^{ + [self->_xpcConnection getProxyHandleWithCompletion:^( + dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { if (handle) { - [handle.proxy invokeCommandWithController:self.controller + [handle.proxy invokeCommandWithController:self.controllerID nodeId:self.nodeID.unsignedLongLongValue endpointId:endpointID clusterId:clusterID @@ -199,6 +257,23 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID }); } }]; + }; + + if (self.controllerID != nil) { + workBlock(); + } else { + [self.controller fetchControllerIdWithQueue:queue + completion:^(id _Nullable controllerID, NSError * _Nullable error) { + if (error != nil) { + // We're already running on the right queue. + completion(nil, error); + return; + } + + self->_controllerID = controllerID; + workBlock(); + }]; + } } - (void)subscribeToAttributesWithEndpointID:(NSNumber * _Nullable)endpointID @@ -210,80 +285,120 @@ - (void)subscribeToAttributesWithEndpointID:(NSNumber * _Nullable)endpointID subscriptionEstablished:(void (^_Nullable)(void))subscriptionEstablishedHandler { MTR_LOG_DEBUG("Subscribing attribute ..."); - [_xpcConnection getProxyHandleWithCompletion:^( - dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { - if (handle) { - MTR_LOG_DEBUG("Setup report handler"); - [self.xpcConnection - registerReportHandlerWithController:self.controller - nodeID:self.nodeID - handler:^(id _Nullable values, NSError * _Nullable error) { - if (values && ![values isKindOfClass:[NSArray class]]) { - MTR_LOG_ERROR("Unsupported report format"); - return; - } - if (!values) { - MTR_LOG_DEBUG("Error report received"); - dispatch_async(queue, ^{ - reportHandler(values, error); - }); - return; - } - __auto_type decodedValues = [MTRDeviceController decodeXPCResponseValues:values]; - NSMutableArray *> * filteredValues = - [NSMutableArray arrayWithCapacity:[decodedValues count]]; - for (NSDictionary * decodedValue in decodedValues) { - MTRAttributePath * attributePath = decodedValue[MTRAttributePathKey]; - if ((endpointID == nil || [attributePath.endpoint isEqualToNumber:endpointID]) - && (clusterID == nil || [attributePath.cluster isEqualToNumber:clusterID]) - && (attributeID == nil || - [attributePath.attribute isEqualToNumber:attributeID])) { - [filteredValues addObject:decodedValue]; + __auto_type workBlock = ^{ + [self->_xpcConnection getProxyHandleWithCompletion:^( + dispatch_queue_t _Nonnull proxyQueue, MTRDeviceControllerXPCProxyHandle * _Nullable handle) { + if (handle) { + MTR_LOG_DEBUG("Setup report handler"); + [self.xpcConnection + registerReportHandlerWithController:self.controllerID + nodeID:self.nodeID + handler:^(id _Nullable values, NSError * _Nullable error) { + if (values && ![values isKindOfClass:[NSArray class]]) { + MTR_LOG_ERROR("Unsupported report format"); + return; } - } - if ([filteredValues count] > 0) { - MTR_LOG_DEBUG("Report received"); - dispatch_async(queue, ^{ - reportHandler(filteredValues, error); - }); - } + if (!values) { + MTR_LOG_DEBUG("Error report received"); + dispatch_async(queue, ^{ + reportHandler(values, error); + }); + return; + } + __auto_type decodedValues = + [MTRDeviceController decodeXPCResponseValues:values]; + NSMutableArray *> * filteredValues = + [NSMutableArray arrayWithCapacity:[decodedValues count]]; + for (NSDictionary * decodedValue in decodedValues) { + MTRAttributePath * attributePath = decodedValue[MTRAttributePathKey]; + if ((endpointID == nil || + [attributePath.endpoint isEqualToNumber:endpointID]) + && (clusterID == nil || + [attributePath.cluster isEqualToNumber:clusterID]) + && (attributeID == nil || + [attributePath.attribute isEqualToNumber:attributeID])) { + [filteredValues addObject:decodedValue]; + } + } + if ([filteredValues count] > 0) { + MTR_LOG_DEBUG("Report received"); + dispatch_async(queue, ^{ + reportHandler(filteredValues, error); + }); + } + }]; + + [handle.proxy subscribeAttributeWithController:self.controllerID + nodeId:self.nodeID.unsignedLongLongValue + endpointId:endpointID + clusterId:clusterID + attributeId:attributeID + minInterval:params.minInterval + maxInterval:params.maxInterval + params:[MTRDeviceController encodeXPCSubscribeParams:params] + establishedHandler:^{ + dispatch_async(queue, ^{ + MTR_LOG_DEBUG("Subscription established"); + subscriptionEstablishedHandler(); + // The following captures the proxy handle in the closure so that the handle + // won't be released prior to block call. + __auto_type handleRetainer = handle; + (void) handleRetainer; + }); }]; - [handle.proxy subscribeAttributeWithController:self.controller - nodeId:self.nodeID.unsignedLongLongValue - endpointId:endpointID - clusterId:clusterID - attributeId:attributeID - minInterval:params.minInterval - maxInterval:params.maxInterval - params:[MTRDeviceController encodeXPCSubscribeParams:params] - establishedHandler:^{ - dispatch_async(queue, ^{ - MTR_LOG_DEBUG("Subscription established"); - subscriptionEstablishedHandler(); - // The following captures the proxy handle in the closure so that the handle - // won't be released prior to block call. - __auto_type handleRetainer = handle; - (void) handleRetainer; - }); - }]; - } else { - dispatch_async(queue, ^{ - MTR_LOG_ERROR("Failed to obtain XPC connection to subscribe to attribute"); - subscriptionEstablishedHandler(); - reportHandler(nil, [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil]); - }); - } - }]; + } else { + dispatch_async(queue, ^{ + MTR_LOG_ERROR("Failed to obtain XPC connection to subscribe to attribute"); + subscriptionEstablishedHandler(); + reportHandler(nil, [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil]); + }); + } + }]; + }; + + if (self.controllerID != nil) { + workBlock(); + } else { + [self.controller fetchControllerIdWithQueue:queue + completion:^(id _Nullable controllerID, NSError * _Nullable error) { + if (error != nil) { + // We're already running on the right queue. + reportHandler(nil, error); + return; + } + + self->_controllerID = controllerID; + workBlock(); + }]; + } } - (void)deregisterReportHandlersWithQueue:(dispatch_queue_t)queue completion:(void (^)(void))completion { MTR_LOG_DEBUG("Deregistering report handlers"); - [_xpcConnection deregisterReportHandlersWithController:self.controller - nodeID:self.nodeID - completion:^{ - dispatch_async(queue, completion); - }]; + __auto_type workBlock = ^{ + [self->_xpcConnection deregisterReportHandlersWithController:self.controllerID + nodeID:self.nodeID + completion:^{ + dispatch_async(queue, completion); + }]; + }; + + if (self.controllerID != nil) { + workBlock(); + } else { + [self.controller fetchControllerIdWithQueue:queue + completion:^(id _Nullable controllerID, NSError * _Nullable error) { + if (error != nil) { + // We're already running on the right queue. + completion(); + return; + } + + self->_controllerID = controllerID; + workBlock(); + }]; + } } - (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode diff --git a/src/darwin/Framework/CHIP/MTRSetupPayload.h b/src/darwin/Framework/CHIP/MTRSetupPayload.h index cbd7b1650bead0..3b50b86a974f46 100644 --- a/src/darwin/Framework/CHIP/MTRSetupPayload.h +++ b/src/darwin/Framework/CHIP/MTRSetupPayload.h @@ -137,6 +137,9 @@ typedef NS_ENUM(NSUInteger, MTROptionalQRCodeInfoType) { @property (nonatomic, copy, nullable) NSNumber * rendezvousInformation MTR_NEWLY_DEPRECATED("Please use discoveryCapabilities"); @property (nonatomic, copy) NSNumber * setUpPINCode MTR_NEWLY_DEPRECATED("Please use setupPasscode"); +- (instancetype)init MTR_NEWLY_DEPRECATED("Please use initWithSetupPasscode or setupPayloadWithOnboardingPayload"); ++ (instancetype)new MTR_NEWLY_DEPRECATED("Please use initWithSetupPasscode or setupPayloadWithOnboardingPayload"); + @end NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRSetupPayload.mm b/src/darwin/Framework/CHIP/MTRSetupPayload.mm index 054c76c55ceb75..e3c886a089f972 100644 --- a/src/darwin/Framework/CHIP/MTRSetupPayload.mm +++ b/src/darwin/Framework/CHIP/MTRSetupPayload.mm @@ -354,7 +354,7 @@ - (NSString * _Nullable)qrCodeString:(NSError * __autoreleasing *)error payload.commissioningFlow = [MTRSetupPayload unconvertCommissioningFlow:self.commissioningFlow]; payload.rendezvousInformation = [MTRSetupPayload convertDiscoveryCapabilities:self.discoveryCapabilities]; payload.discriminator.SetLongValue([self.discriminator unsignedShortValue]); - payload.setUpPINCode = [self.setUpPINCode unsignedIntValue]; + payload.setUpPINCode = [self.setupPasscode unsignedIntValue]; std::string outDecimalString; CHIP_ERROR err = chip::QRCodeSetupPayloadGenerator(payload).payloadBase38Representation(outDecimalString); @@ -432,4 +432,26 @@ - (void)setSetUpPINCode:(NSNumber *)setUpPINCode self.setupPasscode = setUpPINCode; } +- (instancetype)init +{ + if (self = [super init]) { + _version = @(0); // Only supported Matter version so far. + _vendorID = @(0); // Not available. + _productID = @(0); // Not available. + _commissioningFlow = MTRCommissioningFlowStandard; + _discoveryCapabilities = MTRDiscoveryCapabilitiesUnknown; + _hasShortDiscriminator = NO; + _discriminator = @(0); + _setupPasscode = @(11111111); // Invalid passcode + _serialNumber = nil; + } + + return self; +} + ++ (instancetype)new +{ + return [[self alloc] init]; +} + @end diff --git a/src/darwin/Framework/CHIPTests/MTRControllerTests.m b/src/darwin/Framework/CHIPTests/MTRControllerTests.m index 78169202dd9a94..06dfb7a2beb3b7 100644 --- a/src/darwin/Framework/CHIPTests/MTRControllerTests.m +++ b/src/darwin/Framework/CHIPTests/MTRControllerTests.m @@ -284,11 +284,6 @@ - (void)testControllerInvalidAccess [controller shutdown]; XCTAssertFalse([controller isRunning]); - XCTAssertFalse([controller getBaseDevice:1234 - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable chipDevice, NSError * _Nullable error) { - XCTAssertEqual(error.code, MTRErrorCodeInvalidState); - }]); [factory stopControllerFactory]; XCTAssertFalse([factory isRunning]); diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m index a01e9b87a4467d..a3a55d060ae9f7 100644 --- a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m +++ b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m @@ -39,7 +39,6 @@ #define MANUAL_INDIVIDUAL_TEST 0 static const uint16_t kPairingTimeoutInSeconds = 10; -static const uint16_t kCASESetupTimeoutInSeconds = 30; static const uint16_t kTimeoutInSeconds = 3; static const uint64_t kDeviceId = 0x12344321; static NSString * kOnboardingPayload = @"MT:-24J0AFN00KA0648G00"; @@ -61,13 +60,12 @@ static void WaitForCommissionee(XCTestExpectation * expectation) MTRDeviceController * controller = sController; XCTAssertNotNil(controller); - [controller getBaseDevice:kDeviceId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertEqual(error.code, 0); - [expectation fulfill]; - mConnectedDevice = device; - }]; + // For now keep the async dispatch, but could we just + // synchronously fulfill the expectation here? + dispatch_async(dispatch_get_main_queue(), ^{ + [expectation fulfill]; + mConnectedDevice = [MTRBaseDevice deviceWithNodeID:@(kDeviceId) controller:controller]; + }); } static MTRBaseDevice * GetConnectedDevice(void) @@ -101,7 +99,9 @@ - (void)onPairingComplete:(NSError *)error XCTAssertEqual(error.code, 0); NSError * commissionError = nil; - [sController commissionDevice:kDeviceId commissioningParams:[[MTRCommissioningParameters alloc] init] error:&commissionError]; + [sController commissionNodeWithID:@(kDeviceId) + commissioningParams:[[MTRCommissioningParameters alloc] init] + error:&commissionError]; XCTAssertNil(commissionError); // Keep waiting for onCommissioningComplete @@ -200,16 +200,6 @@ - (void)initStack XCTAssertNil(error); [self waitForExpectationsWithTimeout:kPairingTimeoutInSeconds handler:nil]; - - __block XCTestExpectation * connectionExpectation = [self expectationWithDescription:@"CASE established"]; - [controller getBaseDevice:kDeviceId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertEqual(error.code, 0); - [connectionExpectation fulfill]; - connectionExpectation = nil; - }]; - [self waitForExpectationsWithTimeout:kCASESetupTimeoutInSeconds handler:nil]; } - (void)shutdownStack @@ -1131,17 +1121,7 @@ - (void)test013_ReuseChipClusterObject MTRDeviceController * controller = sController; XCTAssertNotNil(controller); - __block MTRBaseDevice * device; - __block XCTestExpectation * connectionExpectation = [self expectationWithDescription:@"CASE established"]; - [controller getBaseDevice:kDeviceId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable retrievedDevice, NSError * _Nullable error) { - XCTAssertEqual(error.code, 0); - [connectionExpectation fulfill]; - connectionExpectation = nil; - device = retrievedDevice; - }]; - [self waitForExpectationsWithTimeout:kCASESetupTimeoutInSeconds handler:nil]; + MTRBaseDevice * device = [MTRBaseDevice deviceWithNodeID:@(kDeviceId) controller:controller]; XCTestExpectation * expectation = [self expectationWithDescription:@"ReuseMTRClusterObjectFirstCall"]; diff --git a/src/darwin/Framework/CHIPTests/MTRXPCListenerSampleTests.m b/src/darwin/Framework/CHIPTests/MTRXPCListenerSampleTests.m index df9006b277fd4f..ad82cd9ab742ae 100644 --- a/src/darwin/Framework/CHIPTests/MTRXPCListenerSampleTests.m +++ b/src/darwin/Framework/CHIPTests/MTRXPCListenerSampleTests.m @@ -41,9 +41,6 @@ // Singleton controller we use. static MTRDeviceController * sController = nil; -// Device we use to send speak CASE to the server. -static MTRBaseDevice * sDevice = nil; - // // Sample XPC Listener implementation that directly communicates with local CHIPDevice instance // @@ -173,25 +170,16 @@ - (void)readAttributeWithController:(id)controller (void) controller; __auto_type sharedController = sController; if (sharedController) { - [sharedController - getBaseDevice:nodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - NSLog(@"Failed to get connected device"); - completion(nil, error); - } else { - [device readAttributeWithEndpointId:endpointId - clusterId:clusterId - attributeId:attributeId - params:[MTRDeviceController decodeXPCReadParams:params] - clientQueue:dispatch_get_main_queue() - completion:^(NSArray *> * _Nullable values, - NSError * _Nullable error) { - completion([MTRDeviceController encodeXPCResponseValues:values], error); - }]; - } - }]; + __auto_type device = [MTRBaseDevice deviceWithNodeID:@(nodeId) controller:sharedController]; + [device + readAttributesWithEndpointID:endpointId + clusterID:clusterId + attributeID:attributeId + params:[MTRDeviceController decodeXPCReadParams:params] + queue:dispatch_get_main_queue() + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + completion([MTRDeviceController encodeXPCResponseValues:values], error); + }]; } else { NSLog(@"Failed to get shared controller"); completion(nil, [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil]); @@ -210,27 +198,17 @@ - (void)writeAttributeWithController:(id)controller (void) controller; __auto_type sharedController = sController; if (sharedController) { - [sharedController - getBaseDevice:nodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - NSLog(@"Failed to get connected device"); - completion(nil, error); - } else { - - [device writeAttributeWithEndpointId:endpointId - clusterId:clusterId - attributeId:attributeId - value:value - timedWriteTimeout:timeoutMs - clientQueue:dispatch_get_main_queue() - completion:^(NSArray *> * _Nullable values, - NSError * _Nullable error) { - completion([MTRDeviceController encodeXPCResponseValues:values], error); - }]; - } - }]; + __auto_type device = [MTRBaseDevice deviceWithNodeID:@(nodeId) controller:sharedController]; + [device + writeAttributeWithEndpointID:endpointId + clusterID:clusterId + attributeID:attributeId + value:value + timedWriteTimeout:timeoutMs + queue:dispatch_get_main_queue() + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + completion([MTRDeviceController encodeXPCResponseValues:values], error); + }]; } else { NSLog(@"Failed to get shared controller"); completion(nil, [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil]); @@ -249,26 +227,17 @@ - (void)invokeCommandWithController:(id)controller (void) controller; __auto_type sharedController = sController; if (sharedController) { - [sharedController - getBaseDevice:nodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - NSLog(@"Failed to get connected device"); - completion(nil, error); - } else { - [device invokeCommandWithEndpointId:endpointId - clusterId:clusterId - commandId:commandId - commandFields:fields - timedInvokeTimeout:nil - clientQueue:dispatch_get_main_queue() - completion:^(NSArray *> * _Nullable values, - NSError * _Nullable error) { - completion([MTRDeviceController encodeXPCResponseValues:values], error); - }]; - } - }]; + __auto_type device = [MTRBaseDevice deviceWithNodeID:@(nodeId) controller:sharedController]; + [device + invokeCommandWithEndpointID:endpointId + clusterID:clusterId + commandID:commandId + commandFields:fields + timedInvokeTimeout:nil + queue:dispatch_get_main_queue() + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + completion([MTRDeviceController encodeXPCResponseValues:values], error); + }]; } else { NSLog(@"Failed to get shared controller"); completion(nil, [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil]); @@ -287,48 +256,28 @@ - (void)subscribeAttributeWithController:(id)controller { __auto_type sharedController = sController; if (sharedController) { - [sharedController - getBaseDevice:nodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - NSLog(@"Failed to get connected device"); - establishedHandler(); - // Send an error report so that the client knows of the failure - [self.clientProxy handleReportWithController:controller - nodeId:nodeId - values:nil - error:[NSError errorWithDomain:MTRErrorDomain - code:MTRErrorCodeGeneralError - userInfo:nil]]; - } else { - __auto_type * subscriptionParams = [MTRDeviceController decodeXPCSubscribeParams:params]; - if (subscriptionParams == nil) { - subscriptionParams = [[MTRSubscribeParams alloc] initWithMinInterval:minInterval - maxInterval:maxInterval]; - } else { - subscriptionParams.minInterval = minInterval; - subscriptionParams.maxInterval = maxInterval; - } - [device subscribeAttributeWithEndpointId:endpointId - clusterId:clusterId - attributeId:attributeId - minInterval:minInterval - maxInterval:maxInterval - params:subscriptionParams - clientQueue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, - NSError * _Nullable error) { - [self.clientProxy - handleReportWithController:controller - nodeId:nodeId - values:[MTRDeviceController - encodeXPCResponseValues:values] - error:error]; - } - subscriptionEstablished:establishedHandler]; - } - }]; + __auto_type * subscriptionParams = [MTRDeviceController decodeXPCSubscribeParams:params]; + if (subscriptionParams == nil) { + subscriptionParams = [[MTRSubscribeParams alloc] initWithMinInterval:minInterval maxInterval:maxInterval]; + } else { + subscriptionParams.minInterval = minInterval; + subscriptionParams.maxInterval = maxInterval; + } + __auto_type device = [MTRBaseDevice deviceWithNodeID:@(nodeId) controller:sharedController]; + [device subscribeToAttributesWithEndpointID:endpointId + clusterID:clusterId + attributeID:attributeId + params:subscriptionParams + queue:dispatch_get_main_queue() + reportHandler:^( + NSArray *> * _Nullable values, NSError * _Nullable error) { + [self.clientProxy + handleReportWithController:controller + nodeId:nodeId + values:[MTRDeviceController encodeXPCResponseValues:values] + error:error]; + } + subscriptionEstablished:establishedHandler]; } else { NSLog(@"Failed to get shared controller"); establishedHandler(); @@ -346,16 +295,8 @@ - (void)stopReportsWithController:(id _Nullable)controller nodeId:(uint64_t)node { __auto_type sharedController = sController; if (sharedController) { - [sharedController getBaseDevice:nodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - NSLog(@"Failed to get connected device"); - } else { - [device deregisterReportHandlersWithClientQueue:dispatch_get_main_queue() - completion:completion]; - } - }]; + __auto_type device = [MTRBaseDevice deviceWithNodeID:@(nodeId) controller:sharedController]; + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() completion:completion]; } else { NSLog(@"Failed to get shared controller"); completion(); @@ -377,50 +318,41 @@ - (void)subscribeWithController:(id _Nullable)controller attributeCacheContainer = [[MTRAttributeCacheContainer alloc] init]; } - [sharedController getBaseDevice:nodeId - queue:dispatch_get_main_queue() - completionHandler:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - NSLog(@"Error: Failed to get connected device (%llu) for attribute cache: %@", nodeId, error); - completion(error); - return; - } - NSMutableArray * established = [NSMutableArray arrayWithCapacity:1]; - [established addObject:@NO]; - __auto_type * subscriptionParams = [MTRDeviceController decodeXPCSubscribeParams:params]; - if (subscriptionParams == nil) { - subscriptionParams = [[MTRSubscribeParams alloc] initWithMinInterval:minInterval - maxInterval:maxInterval]; - } else { - subscriptionParams.minInterval = minInterval; - subscriptionParams.maxInterval = maxInterval; - } - [device subscribeWithQueue:dispatch_get_main_queue() - params:subscriptionParams - attributeCacheContainer:attributeCacheContainer - attributeReportHandler:^(NSArray * value) { - NSLog(@"Received report: %@", value); - } - eventReportHandler:nil - errorHandler:^(NSError * error) { - NSLog(@"Received report error: %@", error); - if (![established[0] boolValue]) { - established[0] = @YES; - completion(error); - } - } - subscriptionEstablished:^() { - NSLog(@"Attribute cache subscription succeeded for device %llu", nodeId); - if (attributeCacheContainer) { - [self.attributeCacheDictionary setObject:attributeCacheContainer forKey:@(nodeId)]; - } - if (![established[0] boolValue]) { - established[0] = @YES; - completion(nil); - } - } - resubscriptionScheduled:nil]; - }]; + __auto_type device = [MTRBaseDevice deviceWithNodeID:@(nodeId) controller:sharedController]; + NSMutableArray * established = [NSMutableArray arrayWithCapacity:1]; + [established addObject:@NO]; + __auto_type * subscriptionParams = [MTRDeviceController decodeXPCSubscribeParams:params]; + if (subscriptionParams == nil) { + subscriptionParams = [[MTRSubscribeParams alloc] initWithMinInterval:minInterval maxInterval:maxInterval]; + } else { + subscriptionParams.minInterval = minInterval; + subscriptionParams.maxInterval = maxInterval; + } + [device subscribeWithQueue:dispatch_get_main_queue() + params:subscriptionParams + attributeCacheContainer:attributeCacheContainer + attributeReportHandler:^(NSArray * value) { + NSLog(@"Received report: %@", value); + } + eventReportHandler:nil + errorHandler:^(NSError * error) { + NSLog(@"Received report error: %@", error); + if (![established[0] boolValue]) { + established[0] = @YES; + completion(error); + } + } + subscriptionEstablished:^() { + NSLog(@"Attribute cache subscription succeeded for device %llu", nodeId); + if (attributeCacheContainer) { + [self.attributeCacheDictionary setObject:attributeCacheContainer forKey:@(nodeId)]; + } + if (![established[0] boolValue]) { + established[0] = @YES; + completion(nil); + } + } + resubscriptionScheduled:nil]; } else { NSLog(@"Failed to get shared controller"); completion([NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil]); @@ -453,7 +385,6 @@ - (void)readAttributeCacheWithController:(id _Nullable)controller @end static const uint16_t kPairingTimeoutInSeconds = 10; -static const uint16_t kCASESetupTimeoutInSeconds = 30; static const uint16_t kTimeoutInSeconds = 3; static const uint64_t kDeviceId = 0x12344321; static NSString * kOnboardingPayload = @"MT:-24J0AFN00KA0648G00"; @@ -489,7 +420,9 @@ - (void)onPairingComplete:(NSError *)error { XCTAssertEqual(error.code, 0); NSError * commissionError = nil; - [sController commissionDevice:kDeviceId commissioningParams:[[MTRCommissioningParameters alloc] init] error:&commissionError]; + [sController commissionNodeWithID:@(kDeviceId) + commissioningParams:[[MTRCommissioningParameters alloc] init] + error:&commissionError]; XCTAssertNil(commissionError); // Keep waiting for onCommissioningComplete @@ -567,17 +500,6 @@ - (void)initStack [self waitForExpectationsWithTimeout:kPairingTimeoutInSeconds handler:nil]; - __block XCTestExpectation * connectionExpectation = [self expectationWithDescription:@"CASE established"]; - [controller getBaseDevice:kDeviceId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertEqual(error.code, 0); - sDevice = device; - [connectionExpectation fulfill]; - connectionExpectation = nil; - }]; - [self waitForExpectationsWithTimeout:kCASESetupTimeoutInSeconds handler:nil]; - mSampleListener = [[MTRXPCListenerSample alloc] init]; [mSampleListener start]; } @@ -600,9 +522,6 @@ - (void)shutdownStack - (void)waitForCommissionee { - XCTestExpectation * expectation = [self expectationWithDescription:@"Wait for the commissioned device to be retrieved"]; - - dispatch_queue_t queue = dispatch_get_main_queue(); __auto_type remoteController = [MTRDeviceController sharedControllerWithID:MTRDeviceControllerId xpcConnectBlock:^NSXPCConnection * _Nonnull { @@ -612,13 +531,7 @@ - (void)waitForCommissionee NSLog(@"Listener is not active"); return nil; }]; - [remoteController getBaseDevice:kDeviceId - queue:queue - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - mConnectedDevice = device; - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; + mConnectedDevice = [MTRBaseDevice deviceWithNodeID:@(kDeviceId) controller:remoteController]; mDeviceController = remoteController; } @@ -1910,7 +1823,7 @@ - (void)test999_TearDown { // Put the device back in the state we found it: open commissioning window, no fabrics commissioned. dispatch_queue_t queue = dispatch_get_main_queue(); - MTRBaseDevice * device = sDevice; + __auto_type device = [MTRBaseDevice deviceWithNodeID:@(kDeviceId) controller:sController]; // Get our current fabric index, for later deletion. XCTestExpectation * readFabricIndexExpectation = [self expectationWithDescription:@"Fabric index read"]; diff --git a/src/darwin/Framework/CHIPTests/MTRXPCProtocolTests.m b/src/darwin/Framework/CHIPTests/MTRXPCProtocolTests.m index 1ab8f162875067..c7ef91da035fed 100644 --- a/src/darwin/Framework/CHIPTests/MTRXPCProtocolTests.m +++ b/src/darwin/Framework/CHIPTests/MTRXPCProtocolTests.m @@ -92,49 +92,39 @@ - (void)subscribeWithDeviceController:(MTRDeviceController *)deviceController queue:queue completion:(void (^)(NSError * _Nullable error))completion { - __auto_type workQueue = dispatch_get_main_queue(); __auto_type completionHandler = ^(NSError * _Nullable error) { dispatch_async(queue, ^{ completion(error); }); }; - [deviceController - getBaseDevice:deviceID.unsignedLongLongValue - queue:workQueue - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - NSLog(@"Error: Failed to get connected device (%llu) for attribute cache: %@", deviceID.unsignedLongLongValue, - error); - completionHandler(error); - return; - } - __auto_type * subscriptionParams - = (params == nil) ? [[MTRSubscribeParams alloc] initWithMinInterval:@(1) maxInterval:@(43200)] : params; - __auto_type established = [NSMutableArray arrayWithCapacity:1]; - [established addObject:@NO]; - [device subscribeWithQueue:queue - params:subscriptionParams - attributeCacheContainer:self - attributeReportHandler:^(NSArray * value) { - NSLog(@"Report received for attribute cache: %@", value); - } - eventReportHandler:nil - errorHandler:^(NSError * error) { - NSLog(@"Report error received for attribute cache: %@", error); - if (![established[0] boolValue]) { - established[0] = @YES; - completionHandler(error); - } - } - subscriptionEstablished:^() { - NSLog(@"Attribute cache subscription succeeded for device %llu", deviceID.unsignedLongLongValue); - if (![established[0] boolValue]) { - established[0] = @YES; - completionHandler(nil); - } - } - resubscriptionScheduled:nil]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:deviceID controller:deviceController]; + + __auto_type * subscriptionParams + = (params == nil) ? [[MTRSubscribeParams alloc] initWithMinInterval:@(1) maxInterval:@(43200)] : params; + __auto_type established = [NSMutableArray arrayWithCapacity:1]; + [established addObject:@NO]; + [device subscribeWithQueue:queue + params:subscriptionParams + attributeCacheContainer:self + attributeReportHandler:^(NSArray * value) { + NSLog(@"Report received for attribute cache: %@", value); + } + eventReportHandler:nil + errorHandler:^(NSError * error) { + NSLog(@"Report error received for attribute cache: %@", error); + if (![established[0] boolValue]) { + established[0] = @YES; + completionHandler(error); + } + } + subscriptionEstablished:^() { + NSLog(@"Attribute cache subscription succeeded for device %llu", [deviceID unsignedLongLongValue]); + if (![established[0] boolValue]) { + established[0] = @YES; + completionHandler(nil); + } + } + resubscriptionScheduled:nil]; } @end @@ -383,27 +373,21 @@ - (void)testReadAttributeSuccess completion([MTRDeviceController encodeXPCResponseValues:myValues], nil); }; - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Reading..."); - [device readAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:nil - queue:dispatch_get_main_queue() - completion:^(id _Nullable value, NSError * _Nullable error) { - NSLog(@"Read value: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myValues isEqual:value]); - [responseExpectation fulfill]; - self.xpcDisconnectExpectation = - [self expectationWithDescription:@"XPC Disconnected"]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Reading..."); + [device readAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:nil + queue:dispatch_get_main_queue() + completion:^(id _Nullable value, NSError * _Nullable error) { + NSLog(@"Read value: %@", value); + XCTAssertNotNil(value); + XCTAssertNil(error); + XCTAssertTrue([myValues isEqual:value]); + [responseExpectation fulfill]; + self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds]; @@ -445,27 +429,21 @@ - (void)testReadAttributeWithParamsSuccess completion([MTRDeviceController encodeXPCResponseValues:myValues], nil); }; - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Reading..."); - [device readAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:myParams - queue:dispatch_get_main_queue() - completion:^(id _Nullable value, NSError * _Nullable error) { - NSLog(@"Read value: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myValues isEqual:value]); - [responseExpectation fulfill]; - self.xpcDisconnectExpectation = - [self expectationWithDescription:@"XPC Disconnected"]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Reading..."); + [device readAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:myParams + queue:dispatch_get_main_queue() + completion:^(id _Nullable value, NSError * _Nullable error) { + NSLog(@"Read value: %@", value); + XCTAssertNotNil(value); + XCTAssertNil(error); + XCTAssertTrue([myValues isEqual:value]); + [responseExpectation fulfill]; + self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds]; @@ -498,26 +476,20 @@ - (void)testReadAttributeFailure completion(nil, myError); }; - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Reading..."); - [device readAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:nil - queue:dispatch_get_main_queue() - completion:^(id _Nullable value, NSError * _Nullable error) { - NSLog(@"Read value: %@", value); - XCTAssertNil(value); - XCTAssertNotNil(error); - [responseExpectation fulfill]; - self.xpcDisconnectExpectation = - [self expectationWithDescription:@"XPC Disconnected"]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Reading..."); + [device readAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:nil + queue:dispatch_get_main_queue() + completion:^(id _Nullable value, NSError * _Nullable error) { + NSLog(@"Read value: %@", value); + XCTAssertNil(value); + XCTAssertNotNil(error); + [responseExpectation fulfill]; + self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds]; @@ -557,28 +529,22 @@ - (void)testWriteAttributeSuccess completion([MTRDeviceController encodeXPCResponseValues:myResults], nil); }; - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Writing..."); - [device writeAttributeWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - value:myValue - timedWriteTimeout:nil - queue:dispatch_get_main_queue() - completion:^(id _Nullable value, NSError * _Nullable error) { - NSLog(@"Write response: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myResults isEqual:value]); - [responseExpectation fulfill]; - self.xpcDisconnectExpectation = - [self expectationWithDescription:@"XPC Disconnected"]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Writing..."); + [device writeAttributeWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + value:myValue + timedWriteTimeout:nil + queue:dispatch_get_main_queue() + completion:^(id _Nullable value, NSError * _Nullable error) { + NSLog(@"Write response: %@", value); + XCTAssertNotNil(value); + XCTAssertNil(error); + XCTAssertTrue([myResults isEqual:value]); + [responseExpectation fulfill]; + self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds]; @@ -620,28 +586,22 @@ - (void)testTimedWriteAttributeSuccess completion([MTRDeviceController encodeXPCResponseValues:myResults], nil); }; - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Writing..."); - [device writeAttributeWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - value:myValue - timedWriteTimeout:myTimedWriteTimeout - queue:dispatch_get_main_queue() - completion:^(id _Nullable value, NSError * _Nullable error) { - NSLog(@"Write response: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myResults isEqual:value]); - [responseExpectation fulfill]; - self.xpcDisconnectExpectation = - [self expectationWithDescription:@"XPC Disconnected"]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Writing..."); + [device writeAttributeWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + value:myValue + timedWriteTimeout:myTimedWriteTimeout + queue:dispatch_get_main_queue() + completion:^(id _Nullable value, NSError * _Nullable error) { + NSLog(@"Write response: %@", value); + XCTAssertNotNil(value); + XCTAssertNil(error); + XCTAssertTrue([myResults isEqual:value]); + [responseExpectation fulfill]; + self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds]; @@ -676,27 +636,21 @@ - (void)testWriteAttributeFailure completion(nil, myError); }; - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Writing..."); - [device writeAttributeWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - value:myValue - timedWriteTimeout:nil - queue:dispatch_get_main_queue() - completion:^(id _Nullable value, NSError * _Nullable error) { - NSLog(@"Write response: %@", value); - XCTAssertNil(value); - XCTAssertNotNil(error); - [responseExpectation fulfill]; - self.xpcDisconnectExpectation = - [self expectationWithDescription:@"XPC Disconnected"]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Writing..."); + [device writeAttributeWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + value:myValue + timedWriteTimeout:nil + queue:dispatch_get_main_queue() + completion:^(id _Nullable value, NSError * _Nullable error) { + NSLog(@"Write response: %@", value); + XCTAssertNil(value); + XCTAssertNotNil(error); + [responseExpectation fulfill]; + self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds]; @@ -736,28 +690,22 @@ - (void)testInvokeCommandSuccess completion([MTRDeviceController encodeXPCResponseValues:myResults], nil); }; - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Invoking command..."); - [device invokeCommandWithEndpointID:myEndpointId - clusterID:myClusterId - commandID:myCommandId - commandFields:myFields - timedInvokeTimeout:nil - queue:dispatch_get_main_queue() - completion:^(id _Nullable value, NSError * _Nullable error) { - NSLog(@"Command response: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myResults isEqual:value]); - [responseExpectation fulfill]; - self.xpcDisconnectExpectation = - [self expectationWithDescription:@"XPC Disconnected"]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Invoking command..."); + [device invokeCommandWithEndpointID:myEndpointId + clusterID:myClusterId + commandID:myCommandId + commandFields:myFields + timedInvokeTimeout:nil + queue:dispatch_get_main_queue() + completion:^(id _Nullable value, NSError * _Nullable error) { + NSLog(@"Command response: %@", value); + XCTAssertNotNil(value); + XCTAssertNil(error); + XCTAssertTrue([myResults isEqual:value]); + [responseExpectation fulfill]; + self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds]; @@ -799,28 +747,22 @@ - (void)testTimedInvokeCommandSuccess completion([MTRDeviceController encodeXPCResponseValues:myResults], nil); }; - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Invoking command..."); - [device invokeCommandWithEndpointID:myEndpointId - clusterID:myClusterId - commandID:myCommandId - commandFields:myFields - timedInvokeTimeout:myTimedInvokeTimeout - queue:dispatch_get_main_queue() - completion:^(id _Nullable value, NSError * _Nullable error) { - NSLog(@"Command response: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myResults isEqual:value]); - [responseExpectation fulfill]; - self.xpcDisconnectExpectation = - [self expectationWithDescription:@"XPC Disconnected"]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Invoking command..."); + [device invokeCommandWithEndpointID:myEndpointId + clusterID:myClusterId + commandID:myCommandId + commandFields:myFields + timedInvokeTimeout:myTimedInvokeTimeout + queue:dispatch_get_main_queue() + completion:^(id _Nullable value, NSError * _Nullable error) { + NSLog(@"Command response: %@", value); + XCTAssertNotNil(value); + XCTAssertNil(error); + XCTAssertTrue([myResults isEqual:value]); + [responseExpectation fulfill]; + self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds]; @@ -858,27 +800,21 @@ - (void)testInvokeCommandFailure completion(nil, myError); }; - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Invoking command..."); - [device invokeCommandWithEndpointID:myEndpointId - clusterID:myClusterId - commandID:myCommandId - commandFields:myFields - timedInvokeTimeout:nil - queue:dispatch_get_main_queue() - completion:^(id _Nullable value, NSError * _Nullable error) { - NSLog(@"Command response: %@", value); - XCTAssertNil(value); - XCTAssertNotNil(error); - [responseExpectation fulfill]; - self.xpcDisconnectExpectation = - [self expectationWithDescription:@"XPC Disconnected"]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Invoking command..."); + [device invokeCommandWithEndpointID:myEndpointId + clusterID:myClusterId + commandID:myCommandId + commandFields:myFields + timedInvokeTimeout:nil + queue:dispatch_get_main_queue() + completion:^(id _Nullable value, NSError * _Nullable error) { + NSLog(@"Command response: %@", value); + XCTAssertNil(value); + XCTAssertNotNil(error); + [responseExpectation fulfill]; + self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds]; @@ -921,30 +857,24 @@ - (void)testSubscribeAttributeSuccess _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController - getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; - [device subscribeToAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:params - queue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - NSLog(@"Report value: %@", values); - XCTAssertNotNil(values); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqual:values]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Subscribing..."); + [device subscribeToAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:params + queue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqual:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; @@ -984,15 +914,12 @@ - (void)testSubscribeAttributeSuccess }; // Deregister report handler - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - }]; - }]; + device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Deregistering..."); + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + }]; // Wait for disconnection [self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds]; @@ -1038,29 +965,23 @@ - (void)testSubscribeAttributeWithParamsSuccess _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController - getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - [device subscribeToAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:myParams - queue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - NSLog(@"Report value: %@", values); - XCTAssertNotNil(values); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqual:values]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Subscribing..."); + [device subscribeToAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:myParams + queue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqual:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; @@ -1100,15 +1021,12 @@ - (void)testSubscribeAttributeWithParamsSuccess }; // Deregister report handler - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - }]; - }]; + device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Deregistering..."); + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + }]; // Wait for disconnection [self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds]; @@ -1149,30 +1067,24 @@ - (void)testBadlyFormattedReport _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController - getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; - [device subscribeToAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:params - queue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - NSLog(@"Report value: %@", values); - XCTAssertNotNil(values); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqual:values]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Subscribing..."); + [device subscribeToAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:params + queue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqual:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; @@ -1210,15 +1122,12 @@ - (void)testBadlyFormattedReport // Deregister report handler _xpcDisconnectExpectation.inverted = NO; - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - }]; - }]; + device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Deregistering..."); + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + }]; // Wait for disconnection [self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds]; @@ -1260,30 +1169,24 @@ - (void)testReportWithUnrelatedEndpointId _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController - getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; - [device subscribeToAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:params - queue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - NSLog(@"Report value: %@", values); - XCTAssertNotNil(values); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqual:values]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Subscribing..."); + [device subscribeToAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:params + queue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqual:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; @@ -1323,15 +1226,12 @@ - (void)testReportWithUnrelatedEndpointId }; // Deregister report handler - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - }]; - }]; + device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Deregistering..."); + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + }]; // Wait for disconnection [self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds]; @@ -1373,30 +1273,24 @@ - (void)testReportWithUnrelatedClusterId _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController - getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; - [device subscribeToAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:params - queue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - NSLog(@"Report value: %@", values); - XCTAssertNotNil(values); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqual:values]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Subscribing..."); + [device subscribeToAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:params + queue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqual:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; @@ -1436,15 +1330,12 @@ - (void)testReportWithUnrelatedClusterId }; // Deregister report handler - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - }]; - }]; + device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Deregistering..."); + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + }]; // Wait for disconnection [self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds]; @@ -1486,30 +1377,24 @@ - (void)testReportWithUnrelatedAttributeId _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController - getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; - [device subscribeToAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:params - queue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - NSLog(@"Report value: %@", values); - XCTAssertNotNil(values); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqual:values]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Subscribing..."); + [device subscribeToAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:params + queue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqual:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; @@ -1549,15 +1434,12 @@ - (void)testReportWithUnrelatedAttributeId }; // Deregister report handler - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - }]; - }]; + device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Deregistering..."); + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + }]; // Wait for disconnection [self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds]; @@ -1599,30 +1481,24 @@ - (void)testReportWithUnrelatedNode _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController - getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; - [device subscribeToAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:params - queue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - NSLog(@"Report value: %@", values); - XCTAssertNotNil(values); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqual:values]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Subscribing..."); + [device subscribeToAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:params + queue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqual:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; @@ -1662,15 +1538,12 @@ - (void)testReportWithUnrelatedNode }; // Deregister report handler - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - }]; - }]; + device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Deregistering..."); + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + }]; // Wait for disconnection [self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds]; @@ -1711,30 +1584,24 @@ - (void)testSubscribeMultiEndpoints _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController - getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; - [device subscribeToAttributesWithEndpointID:nil - clusterID:myClusterId - attributeID:myAttributeId - params:params - queue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - NSLog(@"Report value: %@", values); - XCTAssertNotNil(values); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqual:values]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Subscribing..."); + [device subscribeToAttributesWithEndpointID:nil + clusterID:myClusterId + attributeID:myAttributeId + params:params + queue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqual:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; @@ -1774,15 +1641,12 @@ - (void)testSubscribeMultiEndpoints }; // Deregister report handler - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - }]; - }]; + device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Deregistering..."); + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + }]; // Wait for disconnection [self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds]; @@ -1823,30 +1687,24 @@ - (void)testSubscribeMultiClusters _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController - getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; - [device subscribeToAttributesWithEndpointID:myEndpointId - clusterID:nil - attributeID:myAttributeId - params:params - queue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - NSLog(@"Report value: %@", values); - XCTAssertNotNil(values); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqual:values]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Subscribing..."); + [device subscribeToAttributesWithEndpointID:myEndpointId + clusterID:nil + attributeID:myAttributeId + params:params + queue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqual:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; @@ -1886,15 +1744,12 @@ - (void)testSubscribeMultiClusters }; // Deregister report handler - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - }]; - }]; + device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Deregistering..."); + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + }]; // Wait for disconnection [self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds]; @@ -1935,30 +1790,24 @@ - (void)testSubscribeMultiAttributes _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController - getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; - [device subscribeToAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:nil - params:params - queue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - NSLog(@"Report value: %@", values); - XCTAssertNotNil(values); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqual:values]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Subscribing..."); + [device subscribeToAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:nil + params:params + queue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqual:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; @@ -1998,15 +1847,12 @@ - (void)testSubscribeMultiAttributes }; // Deregister report handler - [_remoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - }]; - }]; + device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Deregistering..."); + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + }]; // Wait for disconnection [self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds]; @@ -2058,30 +1904,24 @@ - (void)testMutiSubscriptions myMaxInterval = maxIntervals[i]; callExpectation = [self expectationWithDescription:[NSString stringWithFormat:@"XPC call (%u) received", i]]; establishExpectation = [self expectationWithDescription:[NSString stringWithFormat:@"Established (%u) called", i]]; - [_remoteDeviceController - getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; - [device subscribeToAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:params - queue:dispatch_get_main_queue() - reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - NSLog(@"Subscriber [%d] report value: %@", i, values); - XCTAssertNotNil(values); - XCTAssertNil(error); - XCTAssertTrue([myReports[i] isEqual:values]); - [reportExpectations[i] fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Subscribing..."); + [device subscribeToAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:params + queue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Subscriber [%d] report value: %@", i, values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReports[i] isEqual:values]); + [reportExpectations[i] fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; } @@ -2132,16 +1972,12 @@ - (void)testMutiSubscriptions // Deregister report handler for first subscriber __auto_type deregisterExpectation = [self expectationWithDescription:@"First subscriber deregistered"]; - [_remoteDeviceController getBaseDevice:nodeToStop - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - [deregisterExpectation fulfill]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(nodeToStop) controller:_remoteDeviceController]; + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + [deregisterExpectation fulfill]; + }]; [self waitForExpectations:@[ stopExpectation, deregisterExpectation ] timeout:kTimeoutInSeconds]; @@ -2190,16 +2026,13 @@ - (void)testMutiSubscriptions // Deregister report handler for second subscriber __auto_type secondDeregisterExpectation = [self expectationWithDescription:@"Second subscriber deregistered"]; - [_remoteDeviceController getBaseDevice:nodeToStop - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - NSLog(@"Device acquired. Deregistering..."); - [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() - completion:^{ - NSLog(@"Deregistered"); - [secondDeregisterExpectation fulfill]; - }]; - }]; + device = [MTRBaseDevice deviceWithNodeID:@(nodeToStop) controller:_remoteDeviceController]; + NSLog(@"Device acquired. Deregistering..."); + [device deregisterReportHandlersWithQueue:dispatch_get_main_queue() + completion:^{ + NSLog(@"Deregistered"); + [secondDeregisterExpectation fulfill]; + }]; // Wait for deregistration and disconnection [self waitForExpectations:@[ secondDeregisterExpectation, _xpcDisconnectExpectation, stopExpectation ] @@ -2245,6 +2078,31 @@ - (void)testAnySharedRemoteController { NSString * myUUID = [[NSUUID UUID] UUIDString]; uint64_t myNodeId = 9876543210; + NSNumber * myEndpointId = @100; + NSNumber * myClusterId = @200; + NSNumber * myAttributeId = @300; + NSArray * myValues = @[ @{ + @"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + } ]; + + XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; + XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"]; + + _handleReadAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId, + NSNumber * _Nullable attributeId, MTRReadParams * _Nullable params, + void (^completion)(id _Nullable values, NSError * _Nullable error)) { + XCTAssertTrue([controller isEqualToString:myUUID]); + XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId); + XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]); + XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]); + XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]); + XCTAssertNil(params); + [callExpectation fulfill]; + completion([MTRDeviceController encodeXPCResponseValues:myValues], nil); + }; __auto_type unspecifiedRemoteDeviceController = [MTRDeviceController sharedControllerWithID:nil @@ -2259,17 +2117,30 @@ - (void)testAnySharedRemoteController [anySharedRemoteControllerCallExpectation fulfill]; }; - __auto_type deviceAcquired = [self expectationWithDescription:@"Connected device was acquired"]; - [unspecifiedRemoteDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - [deviceAcquired fulfill]; - }]; - - [self waitForExpectations:[NSArray arrayWithObjects:anySharedRemoteControllerCallExpectation, deviceAcquired, nil] + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:unspecifiedRemoteDeviceController]; + // Do a read to exercise the device. + NSLog(@"Device acquired. Reading..."); + [device readAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:nil + queue:dispatch_get_main_queue() + completion:^(id _Nullable value, NSError * _Nullable error) { + NSLog(@"Read value: %@", value); + XCTAssertNotNil(value); + XCTAssertNil(error); + XCTAssertTrue([myValues isEqual:value]); + [responseExpectation fulfill]; + self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; + }]; + + [self waitForExpectations:[NSArray arrayWithObjects:anySharedRemoteControllerCallExpectation, callExpectation, + responseExpectation, nil] timeout:kTimeoutInSeconds]; + + // When read is done, connection should have been released + [self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds]; + XCTAssertNil(_xpcConnection); } - (void)testSubscribeAttributeCacheSuccess @@ -2517,24 +2388,19 @@ - (void)testXPCConnectionFailure return nil; }]; - [failingDeviceController getBaseDevice:myNodeId - queue:dispatch_get_main_queue() - completion:^(MTRBaseDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Reading..."); - [device readAttributesWithEndpointID:myEndpointId - clusterID:myClusterId - attributeID:myAttributeId - params:nil - queue:dispatch_get_main_queue() - completion:^(id _Nullable value, NSError * _Nullable error) { - NSLog(@"Read value: %@", value); - XCTAssertNil(value); - XCTAssertNotNil(error); - [responseExpectation fulfill]; - }]; - }]; + __auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:failingDeviceController]; + NSLog(@"Device acquired. Reading..."); + [device readAttributesWithEndpointID:myEndpointId + clusterID:myClusterId + attributeID:myAttributeId + params:nil + queue:dispatch_get_main_queue() + completion:^(id _Nullable value, NSError * _Nullable error) { + NSLog(@"Read value: %@", value); + XCTAssertNil(value); + XCTAssertNotNil(error); + [responseExpectation fulfill]; + }]; [self waitForExpectations:@[ responseExpectation ] timeout:kTimeoutInSeconds]; }