Skip to content

Commit c169bc6

Browse files
Re-add the openCommissioningWindowWithSetupPasscode APIs on MTRDevice and MTRBaseDevice.
This is a re-landing of the API addition parts of #22521, without including any of the API changes/removals. The code is identical to what was on master before Darwin framework changes were reverted in #23155.
1 parent 12802fb commit c169bc6

10 files changed

+312
-35
lines changed

examples/darwin-framework-tool/commands/pairing/OpenCommissioningWindowCommand.mm

+44-32
Original file line numberDiff line numberDiff line change
@@ -17,47 +17,59 @@
1717

1818
#import <Matter/Matter.h>
1919

20+
#import "MTRError_Utils.h"
21+
2022
#include "OpenCommissioningWindowCommand.h"
2123

2224
CHIP_ERROR OpenCommissioningWindowCommand::RunCommand()
2325
{
26+
mWorkQueue = dispatch_queue_create("com.chip.open_commissioning_window", DISPATCH_QUEUE_SERIAL);
2427
auto * controller = CurrentCommissioner();
25-
NSError * error;
26-
__block NSString * pairingCode;
28+
auto * device = [MTRDevice deviceWithNodeID:mNodeId deviceController:controller];
29+
30+
auto * self = this;
2731
if (mCommissioningWindowOption == 0) {
28-
[controller openPairingWindow:mNodeId duration:mCommissioningWindowTimeoutMs error:&error];
32+
auto * cluster = [[MTRClusterAdministratorCommissioning alloc] initWithDevice:device endpoint:0 queue:mWorkQueue];
33+
auto * params = [[MTRAdministratorCommissioningClusterOpenBasicCommissioningWindowParams alloc] init];
34+
params.commissioningTimeout = @(mCommissioningWindowTimeoutMs);
35+
params.timedInvokeTimeoutMs = @(10000);
36+
[cluster openBasicCommissioningWindowWithParams:params
37+
expectedValues:nil
38+
expectedValueInterval:nil
39+
completionHandler:^(NSError * _Nullable error) {
40+
if (error == nil) {
41+
self->SetCommandExitStatus(CHIP_NO_ERROR);
42+
} else {
43+
self->SetCommandExitStatus(MTRErrorToCHIPErrorCode(error));
44+
}
45+
}];
2946
} else {
30-
pairingCode = [controller openPairingWindowWithPIN:mNodeId
31-
duration:mCommissioningWindowTimeoutMs
32-
discriminator:mDiscriminator
33-
setupPIN:[MTRSetupPayload generateRandomPIN]
34-
error:&error];
35-
}
47+
[device
48+
openCommissioningWindowWithSetupPasscode:[MTRSetupPayload generateRandomSetupPasscode]
49+
discriminator:@(mDiscriminator)
50+
duration:@(mCommissioningWindowTimeoutMs)
51+
queue:mWorkQueue
52+
completion:^(MTRSetupPayload * _Nullable payload, NSError * error) {
53+
if (error != nil) {
54+
self->SetCommandExitStatus(MTRErrorToCHIPErrorCode(error));
55+
return;
56+
}
3657

37-
if (error != nil) {
38-
SetCommandExitStatus(error);
39-
return CHIP_NO_ERROR;
40-
}
58+
if (payload == nil) {
59+
self->SetCommandExitStatus(CHIP_ERROR_INVALID_ARGUMENT);
60+
return;
61+
}
4162

42-
// TODO: Those should be async operations and we should not claim to
43-
// be done until they complete. As things stand, we have no idea
44-
// how to tell when we're done, so just set a timer for slightly
45-
// less than our command timeout to call SetCommandExitStatus.
46-
mWorkQueue = dispatch_queue_create("com.chip.open_commissioning_window", DISPATCH_QUEUE_SERIAL);
47-
mTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, mWorkQueue);
48-
auto * self = this;
49-
dispatch_source_set_event_handler(mTimer, ^{
50-
dispatch_source_cancel(mTimer);
51-
mTimer = nil;
52-
mWorkQueue = nil;
53-
if (pairingCode != nil) {
54-
ChipLogProgress(chipTool, "Setup code: %s\n", [pairingCode UTF8String]);
55-
}
56-
self->SetCommandExitStatus(CHIP_NO_ERROR);
57-
});
58-
dispatch_source_set_timer(
59-
mTimer, dispatch_time(DISPATCH_TIME_NOW, (GetWaitDuration().count() - 2000) * NSEC_PER_MSEC), DISPATCH_TIME_FOREVER, 0);
60-
dispatch_resume(mTimer);
63+
auto * pairingCode = [payload manualEntryCode];
64+
if (pairingCode == nil) {
65+
self->SetCommandExitStatus(CHIP_ERROR_INVALID_ARGUMENT);
66+
return;
67+
}
68+
69+
ChipLogProgress(chipTool, "Setup code: %s\n", [[payload manualEntryCode] UTF8String]);
70+
self->SetCommandExitStatus(CHIP_NO_ERROR);
71+
}];
72+
}
6173

6274
return CHIP_NO_ERROR;
6375
}

src/darwin/Framework/CHIP/MTRBaseDevice.h

+27
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#import <Foundation/Foundation.h>
1919

20+
@class MTRSetupPayload;
21+
2022
NS_ASSUME_NONNULL_BEGIN
2123

2224
/**
@@ -87,6 +89,11 @@ typedef void (^MTRDeviceErrorHandler)(NSError * error);
8789
*/
8890
typedef void (^MTRDeviceResubscriptionScheduledHandler)(NSError * error, NSNumber * resubscriptionDelay);
8991

92+
/**
93+
* Handler for openCommissioningWindowWithSetupPasscode.
94+
*/
95+
typedef void (^MTRDeviceOpenCommissioningWindowHandler)(MTRSetupPayload * _Nullable payload, NSError * _Nullable error);
96+
9097
extern NSString * const MTRAttributePathKey;
9198
extern NSString * const MTRCommandPathKey;
9299
extern NSString * const MTREventPathKey;
@@ -238,6 +245,26 @@ extern NSString * const MTRArrayValueType;
238245
*/
239246
- (void)deregisterReportHandlersWithClientQueue:(dispatch_queue_t)clientQueue completion:(void (^)(void))completion;
240247

248+
/**
249+
* Open a commissioning window on the device.
250+
*
251+
* On success, completion will be called on queue with the MTRSetupPayload that
252+
* can be used to commission the device.
253+
*
254+
* @param setupPasscode The setup passcode to use for the commissioning window.
255+
* See MTRSetupPayload's generateRandomSetupPasscode for
256+
* generating a valid random passcode.
257+
* @param discriminator The discriminator to use for the commissionable
258+
* advertisement.
259+
* @param duration Duration, in seconds, during which the commissioning
260+
* window will be open.
261+
*/
262+
- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode
263+
discriminator:(NSNumber *)discriminator
264+
duration:(NSNumber *)duration
265+
queue:(dispatch_queue_t)queue
266+
completion:(MTRDeviceOpenCommissioningWindowHandler)completion;
267+
241268
@end
242269

243270
@interface MTRAttributePath : NSObject <NSCopying>

src/darwin/Framework/CHIP/MTRBaseDevice.mm

+143
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#import "MTRError_Internal.h"
2525
#import "MTREventTLVValueDecoder_Internal.h"
2626
#import "MTRLogging.h"
27+
#import "MTRSetupPayload_Internal.h"
2728

2829
#include "app/ConcreteAttributePath.h"
2930
#include "app/ConcreteCommandPath.h"
@@ -36,8 +37,12 @@
3637
#include <app/InteractionModelEngine.h>
3738
#include <app/ReadClient.h>
3839
#include <app/util/error-mapping.h>
40+
#include <controller/CommissioningWindowOpener.h>
3941
#include <controller/ReadInteraction.h>
4042
#include <controller/WriteInteraction.h>
43+
#include <crypto/CHIPCryptoPAL.h>
44+
#include <setup_payload/SetupPayload.h>
45+
#include <system/SystemClock.h>
4146

4247
#include <memory>
4348

@@ -1256,6 +1261,144 @@ - (void)deregisterReportHandlersWithClientQueue:(dispatch_queue_t)clientQueue co
12561261
PurgeReadClientContainers(self.nodeID, clientQueue, completion);
12571262
}
12581263

1264+
namespace {
1265+
class OpenCommissioningWindowHelper {
1266+
typedef void (^ResultCallback)(CHIP_ERROR status, const SetupPayload &);
1267+
1268+
public:
1269+
static CHIP_ERROR OpenCommissioningWindow(Controller::DeviceController * controller, NodeId nodeID,
1270+
System::Clock::Seconds16 timeout, uint16_t discriminator, uint32_t setupPIN, ResultCallback callback);
1271+
1272+
private:
1273+
OpenCommissioningWindowHelper(Controller::DeviceController * controller, ResultCallback callback);
1274+
1275+
static void OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload);
1276+
1277+
Controller::CommissioningWindowOpener mOpener;
1278+
Callback::Callback<Controller::OnOpenCommissioningWindow> mOnOpenCommissioningWindowCallback;
1279+
ResultCallback mResultCallback;
1280+
};
1281+
1282+
OpenCommissioningWindowHelper::OpenCommissioningWindowHelper(Controller::DeviceController * controller, ResultCallback callback)
1283+
: mOpener(controller)
1284+
, mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this)
1285+
, mResultCallback(callback)
1286+
{
1287+
}
1288+
1289+
CHIP_ERROR OpenCommissioningWindowHelper::OpenCommissioningWindow(Controller::DeviceController * controller, NodeId nodeID,
1290+
System::Clock::Seconds16 timeout, uint16_t discriminator, uint32_t setupPIN, ResultCallback callback)
1291+
{
1292+
auto * self = new (std::nothrow) OpenCommissioningWindowHelper(controller, callback);
1293+
if (self == nullptr) {
1294+
return CHIP_ERROR_NO_MEMORY;
1295+
}
1296+
1297+
SetupPayload unused;
1298+
CHIP_ERROR err = self->mOpener.OpenCommissioningWindow(nodeID, timeout, Crypto::kSpake2p_Min_PBKDF_Iterations, discriminator,
1299+
MakeOptional(setupPIN), NullOptional, &self->mOnOpenCommissioningWindowCallback, unused);
1300+
if (err != CHIP_NO_ERROR) {
1301+
delete self;
1302+
}
1303+
// Else will clean up when the callback is called.
1304+
return err;
1305+
}
1306+
1307+
void OpenCommissioningWindowHelper::OnOpenCommissioningWindowResponse(
1308+
void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload)
1309+
{
1310+
auto * self = static_cast<OpenCommissioningWindowHelper *>(context);
1311+
self->mResultCallback(status, payload);
1312+
delete self;
1313+
}
1314+
1315+
} // anonymous namespace
1316+
1317+
- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode
1318+
discriminator:(NSNumber *)discriminator
1319+
duration:(NSNumber *)duration
1320+
queue:(dispatch_queue_t)queue
1321+
completion:(MTRDeviceOpenCommissioningWindowHandler)completion
1322+
{
1323+
if (self.isPASEDevice) {
1324+
MTR_LOG_ERROR("Can't open a commissioning window over PASE");
1325+
dispatch_async(queue, ^{
1326+
completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE]);
1327+
});
1328+
return;
1329+
}
1330+
1331+
unsigned long long durationVal = [duration unsignedLongLongValue];
1332+
if (!CanCastTo<uint16_t>(durationVal)) {
1333+
MTR_LOG_ERROR("Error: Duration %llu is too large.", durationVal);
1334+
dispatch_async(queue, ^{
1335+
completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]);
1336+
});
1337+
return;
1338+
}
1339+
1340+
unsigned long long discriminatorVal = [discriminator unsignedLongLongValue];
1341+
1342+
if (discriminatorVal > 0xFFF) {
1343+
MTR_LOG_ERROR("Error: Discriminator %llu is too large. Max value %d", discriminatorVal, 0xFFF);
1344+
dispatch_async(queue, ^{
1345+
completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]);
1346+
});
1347+
return;
1348+
}
1349+
1350+
unsigned long long passcodeVal = [setupPasscode unsignedLongLongValue];
1351+
if (!CanCastTo<uint32_t>(passcodeVal) || !SetupPayload::IsValidSetupPIN(static_cast<uint32_t>(passcodeVal))) {
1352+
MTR_LOG_ERROR("Error: Setup passcode %llu is not valid", passcodeVal);
1353+
dispatch_async(queue, ^{
1354+
completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]);
1355+
});
1356+
return;
1357+
}
1358+
1359+
[self.deviceController
1360+
asyncDispatchToMatterQueue:^(Controller::DeviceCommissioner * commissioner) {
1361+
auto resultCallback = ^(CHIP_ERROR status, const SetupPayload & payload) {
1362+
if (status != CHIP_NO_ERROR) {
1363+
dispatch_async(queue, ^{
1364+
completion(nil, [MTRError errorForCHIPErrorCode:status]);
1365+
});
1366+
return;
1367+
}
1368+
auto * payloadObj = [[MTRSetupPayload alloc] initWithSetupPayload:payload];
1369+
if (payloadObj == nil) {
1370+
dispatch_async(queue, ^{
1371+
completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_NO_MEMORY]);
1372+
});
1373+
return;
1374+
}
1375+
1376+
dispatch_async(queue, ^{
1377+
completion(payloadObj, nil);
1378+
});
1379+
};
1380+
1381+
SetupPayload setupPayload;
1382+
auto errorCode = OpenCommissioningWindowHelper::OpenCommissioningWindow(commissioner, self.nodeID,
1383+
chip::System::Clock::Seconds16(static_cast<uint16_t>(durationVal)), static_cast<uint16_t>(discriminatorVal),
1384+
static_cast<uint32_t>(passcodeVal), resultCallback);
1385+
1386+
if (errorCode != CHIP_NO_ERROR) {
1387+
dispatch_async(queue, ^{
1388+
completion(nil, [MTRError errorForCHIPErrorCode:errorCode]);
1389+
});
1390+
return;
1391+
}
1392+
1393+
// resultCallback will handle things now.
1394+
}
1395+
errorHandler:^(NSError * error) {
1396+
dispatch_async(queue, ^{
1397+
completion(nil, error);
1398+
});
1399+
}];
1400+
}
1401+
12591402
#ifdef DEBUG
12601403
// This method is for unit testing only
12611404
- (void)failSubscribers:(dispatch_queue_t)clientQueue completion:(void (^)(void))completion

src/darwin/Framework/CHIP/MTRDevice.h

+21-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ typedef NS_ENUM(NSUInteger, MTRDeviceState) {
5050
* The current state of the device.
5151
*
5252
* The three states:
53-
* MTRDeviceStateUnknkown
53+
* MTRDeviceStateUnknown
5454
* Unable to determine the state of the device at the moment.
5555
*
5656
* MTRDeviceStateReachable
@@ -137,6 +137,26 @@ typedef NS_ENUM(NSUInteger, MTRDeviceState) {
137137
clientQueue:(dispatch_queue_t)clientQueue
138138
completion:(MTRDeviceResponseHandler)completion;
139139

140+
/**
141+
* Open a commissioning window on the device.
142+
*
143+
* On success, completion will be called on queue with the MTRSetupPayload that
144+
* can be used to commission the device.
145+
*
146+
* @param setupPasscode The setup passcode to use for the commissioning window.
147+
* See MTRSetupPayload's generateRandomSetupPasscode for
148+
* generating a valid random passcode.
149+
* @param discriminator The discriminator to use for the commissionable
150+
* advertisement.
151+
* @param duration Duration, in seconds, during which the commissioning
152+
* window will be open.
153+
*/
154+
- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode
155+
discriminator:(NSNumber *)discriminator
156+
duration:(NSNumber *)duration
157+
queue:(dispatch_queue_t)queue
158+
completion:(MTRDeviceOpenCommissioningWindowHandler)completion;
159+
140160
@end
141161

142162
@protocol MTRDeviceDelegate <NSObject>

src/darwin/Framework/CHIP/MTRDevice.mm

+14
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,20 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
474474
[self setExpectedValues:expectedValues expectedValueInterval:expectedValueInterval];
475475
}
476476

477+
- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode
478+
discriminator:(NSNumber *)discriminator
479+
duration:(NSNumber *)duration
480+
queue:(dispatch_queue_t)queue
481+
completion:(MTRDeviceOpenCommissioningWindowHandler)completion
482+
{
483+
auto * baseDevice = [[MTRBaseDevice alloc] initWithNodeID:self.nodeID controller:self.deviceController];
484+
[baseDevice openCommissioningWindowWithSetupPasscode:setupPasscode
485+
discriminator:discriminator
486+
duration:duration
487+
queue:queue
488+
completion:completion];
489+
}
490+
477491
#pragma mark - Cache management
478492

479493
// assume lock is held

src/darwin/Framework/CHIP/MTRDeviceController.mm

+22
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,28 @@ - (BOOL)getSessionForCommissioneeDevice:(chip::NodeId)deviceID completion:(MTRIn
886886
return YES;
887887
}
888888

889+
- (void)asyncDispatchToMatterQueue:(void (^)(chip::Controller::DeviceCommissioner *))block
890+
errorHandler:(void (^)(NSError *))errorHandler
891+
{
892+
{
893+
NSError * error;
894+
if (![self checkIsRunning:&error]) {
895+
errorHandler(error);
896+
return;
897+
}
898+
}
899+
900+
dispatch_async(_chipWorkQueue, ^{
901+
NSError * error;
902+
if (![self checkIsRunning:&error]) {
903+
errorHandler(error);
904+
return;
905+
}
906+
907+
block(self.cppCommissioner);
908+
});
909+
}
910+
889911
@end
890912

891913
@implementation MTRDeviceController (InternalMethods)

0 commit comments

Comments
 (0)