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]; }