Skip to content

Commit

Permalink
Re-add the openCommissioningWindowWithSetupPasscode APIs on MTRDevice…
Browse files Browse the repository at this point in the history
… and MTRBaseDevice. (#23157)

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.
  • Loading branch information
bzbarsky-apple authored Oct 13, 2022
1 parent 910218a commit 2c9cd92
Show file tree
Hide file tree
Showing 10 changed files with 312 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,59 @@

#import <Matter/Matter.h>

#import "MTRError_Utils.h"

#include "OpenCommissioningWindowCommand.h"

CHIP_ERROR OpenCommissioningWindowCommand::RunCommand()
{
mWorkQueue = dispatch_queue_create("com.chip.open_commissioning_window", DISPATCH_QUEUE_SERIAL);
auto * controller = CurrentCommissioner();
NSError * error;
__block NSString * pairingCode;
auto * device = [MTRDevice deviceWithNodeID:mNodeId deviceController:controller];

auto * self = this;
if (mCommissioningWindowOption == 0) {
[controller openPairingWindow:mNodeId duration:mCommissioningWindowTimeoutMs error:&error];
auto * cluster = [[MTRClusterAdministratorCommissioning alloc] initWithDevice:device endpoint:0 queue:mWorkQueue];
auto * params = [[MTRAdministratorCommissioningClusterOpenBasicCommissioningWindowParams alloc] init];
params.commissioningTimeout = @(mCommissioningWindowTimeoutMs);
params.timedInvokeTimeoutMs = @(10000);
[cluster openBasicCommissioningWindowWithParams:params
expectedValues:nil
expectedValueInterval:nil
completionHandler:^(NSError * _Nullable error) {
if (error == nil) {
self->SetCommandExitStatus(CHIP_NO_ERROR);
} else {
self->SetCommandExitStatus(MTRErrorToCHIPErrorCode(error));
}
}];
} else {
pairingCode = [controller openPairingWindowWithPIN:mNodeId
duration:mCommissioningWindowTimeoutMs
discriminator:mDiscriminator
setupPIN:[MTRSetupPayload generateRandomPIN]
error:&error];
}
[device
openCommissioningWindowWithSetupPasscode:[MTRSetupPayload generateRandomSetupPasscode]
discriminator:@(mDiscriminator)
duration:@(mCommissioningWindowTimeoutMs)
queue:mWorkQueue
completion:^(MTRSetupPayload * _Nullable payload, NSError * error) {
if (error != nil) {
self->SetCommandExitStatus(MTRErrorToCHIPErrorCode(error));
return;
}

if (error != nil) {
SetCommandExitStatus(error);
return CHIP_NO_ERROR;
}
if (payload == nil) {
self->SetCommandExitStatus(CHIP_ERROR_INVALID_ARGUMENT);
return;
}

// TODO: Those should be async operations and we should not claim to
// be done until they complete. As things stand, we have no idea
// how to tell when we're done, so just set a timer for slightly
// less than our command timeout to call SetCommandExitStatus.
mWorkQueue = dispatch_queue_create("com.chip.open_commissioning_window", DISPATCH_QUEUE_SERIAL);
mTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, mWorkQueue);
auto * self = this;
dispatch_source_set_event_handler(mTimer, ^{
dispatch_source_cancel(mTimer);
mTimer = nil;
mWorkQueue = nil;
if (pairingCode != nil) {
ChipLogProgress(chipTool, "Setup code: %s\n", [pairingCode UTF8String]);
}
self->SetCommandExitStatus(CHIP_NO_ERROR);
});
dispatch_source_set_timer(
mTimer, dispatch_time(DISPATCH_TIME_NOW, (GetWaitDuration().count() - 2000) * NSEC_PER_MSEC), DISPATCH_TIME_FOREVER, 0);
dispatch_resume(mTimer);
auto * pairingCode = [payload manualEntryCode];
if (pairingCode == nil) {
self->SetCommandExitStatus(CHIP_ERROR_INVALID_ARGUMENT);
return;
}

ChipLogProgress(chipTool, "Setup code: %s\n", [[payload manualEntryCode] UTF8String]);
self->SetCommandExitStatus(CHIP_NO_ERROR);
}];
}

return CHIP_NO_ERROR;
}
27 changes: 27 additions & 0 deletions src/darwin/Framework/CHIP/MTRBaseDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

#import <Foundation/Foundation.h>

@class MTRSetupPayload;

NS_ASSUME_NONNULL_BEGIN

/**
Expand Down Expand Up @@ -87,6 +89,11 @@ typedef void (^MTRDeviceErrorHandler)(NSError * error);
*/
typedef void (^MTRDeviceResubscriptionScheduledHandler)(NSError * error, NSNumber * resubscriptionDelay);

/**
* Handler for openCommissioningWindowWithSetupPasscode.
*/
typedef void (^MTRDeviceOpenCommissioningWindowHandler)(MTRSetupPayload * _Nullable payload, NSError * _Nullable error);

extern NSString * const MTRAttributePathKey;
extern NSString * const MTRCommandPathKey;
extern NSString * const MTREventPathKey;
Expand Down Expand Up @@ -238,6 +245,26 @@ extern NSString * const MTRArrayValueType;
*/
- (void)deregisterReportHandlersWithClientQueue:(dispatch_queue_t)clientQueue completion:(void (^)(void))completion;

/**
* Open a commissioning window on the device.
*
* On success, completion will be called on queue with the MTRSetupPayload that
* can be used to commission the device.
*
* @param setupPasscode The setup passcode to use for the commissioning window.
* See MTRSetupPayload's generateRandomSetupPasscode for
* generating a valid random passcode.
* @param discriminator The discriminator to use for the commissionable
* advertisement.
* @param duration Duration, in seconds, during which the commissioning
* window will be open.
*/
- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode
discriminator:(NSNumber *)discriminator
duration:(NSNumber *)duration
queue:(dispatch_queue_t)queue
completion:(MTRDeviceOpenCommissioningWindowHandler)completion;

@end

@interface MTRAttributePath : NSObject <NSCopying>
Expand Down
143 changes: 143 additions & 0 deletions src/darwin/Framework/CHIP/MTRBaseDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#import "MTRError_Internal.h"
#import "MTREventTLVValueDecoder_Internal.h"
#import "MTRLogging.h"
#import "MTRSetupPayload_Internal.h"

#include "app/ConcreteAttributePath.h"
#include "app/ConcreteCommandPath.h"
Expand All @@ -36,8 +37,12 @@
#include <app/InteractionModelEngine.h>
#include <app/ReadClient.h>
#include <app/util/error-mapping.h>
#include <controller/CommissioningWindowOpener.h>
#include <controller/ReadInteraction.h>
#include <controller/WriteInteraction.h>
#include <crypto/CHIPCryptoPAL.h>
#include <setup_payload/SetupPayload.h>
#include <system/SystemClock.h>

#include <memory>

Expand Down Expand Up @@ -1256,6 +1261,144 @@ - (void)deregisterReportHandlersWithClientQueue:(dispatch_queue_t)clientQueue co
PurgeReadClientContainers(self.nodeID, clientQueue, completion);
}

namespace {
class OpenCommissioningWindowHelper {
typedef void (^ResultCallback)(CHIP_ERROR status, const SetupPayload &);

public:
static CHIP_ERROR OpenCommissioningWindow(Controller::DeviceController * controller, NodeId nodeID,
System::Clock::Seconds16 timeout, uint16_t discriminator, uint32_t setupPIN, ResultCallback callback);

private:
OpenCommissioningWindowHelper(Controller::DeviceController * controller, ResultCallback callback);

static void OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload);

Controller::CommissioningWindowOpener mOpener;
Callback::Callback<Controller::OnOpenCommissioningWindow> mOnOpenCommissioningWindowCallback;
ResultCallback mResultCallback;
};

OpenCommissioningWindowHelper::OpenCommissioningWindowHelper(Controller::DeviceController * controller, ResultCallback callback)
: mOpener(controller)
, mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this)
, mResultCallback(callback)
{
}

CHIP_ERROR OpenCommissioningWindowHelper::OpenCommissioningWindow(Controller::DeviceController * controller, NodeId nodeID,
System::Clock::Seconds16 timeout, uint16_t discriminator, uint32_t setupPIN, ResultCallback callback)
{
auto * self = new (std::nothrow) OpenCommissioningWindowHelper(controller, callback);
if (self == nullptr) {
return CHIP_ERROR_NO_MEMORY;
}

SetupPayload unused;
CHIP_ERROR err = self->mOpener.OpenCommissioningWindow(nodeID, timeout, Crypto::kSpake2p_Min_PBKDF_Iterations, discriminator,
MakeOptional(setupPIN), NullOptional, &self->mOnOpenCommissioningWindowCallback, unused);
if (err != CHIP_NO_ERROR) {
delete self;
}
// Else will clean up when the callback is called.
return err;
}

void OpenCommissioningWindowHelper::OnOpenCommissioningWindowResponse(
void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload)
{
auto * self = static_cast<OpenCommissioningWindowHelper *>(context);
self->mResultCallback(status, payload);
delete self;
}

} // anonymous namespace

- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode
discriminator:(NSNumber *)discriminator
duration:(NSNumber *)duration
queue:(dispatch_queue_t)queue
completion:(MTRDeviceOpenCommissioningWindowHandler)completion
{
if (self.isPASEDevice) {
MTR_LOG_ERROR("Can't open a commissioning window over PASE");
dispatch_async(queue, ^{
completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE]);
});
return;
}

unsigned long long durationVal = [duration unsignedLongLongValue];
if (!CanCastTo<uint16_t>(durationVal)) {
MTR_LOG_ERROR("Error: Duration %llu is too large.", durationVal);
dispatch_async(queue, ^{
completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]);
});
return;
}

unsigned long long discriminatorVal = [discriminator unsignedLongLongValue];

if (discriminatorVal > 0xFFF) {
MTR_LOG_ERROR("Error: Discriminator %llu is too large. Max value %d", discriminatorVal, 0xFFF);
dispatch_async(queue, ^{
completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]);
});
return;
}

unsigned long long passcodeVal = [setupPasscode unsignedLongLongValue];
if (!CanCastTo<uint32_t>(passcodeVal) || !SetupPayload::IsValidSetupPIN(static_cast<uint32_t>(passcodeVal))) {
MTR_LOG_ERROR("Error: Setup passcode %llu is not valid", passcodeVal);
dispatch_async(queue, ^{
completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_INTEGER_VALUE]);
});
return;
}

[self.deviceController
asyncDispatchToMatterQueue:^(Controller::DeviceCommissioner * commissioner) {
auto resultCallback = ^(CHIP_ERROR status, const SetupPayload & payload) {
if (status != CHIP_NO_ERROR) {
dispatch_async(queue, ^{
completion(nil, [MTRError errorForCHIPErrorCode:status]);
});
return;
}
auto * payloadObj = [[MTRSetupPayload alloc] initWithSetupPayload:payload];
if (payloadObj == nil) {
dispatch_async(queue, ^{
completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_NO_MEMORY]);
});
return;
}

dispatch_async(queue, ^{
completion(payloadObj, nil);
});
};

SetupPayload setupPayload;
auto errorCode = OpenCommissioningWindowHelper::OpenCommissioningWindow(commissioner, self.nodeID,
chip::System::Clock::Seconds16(static_cast<uint16_t>(durationVal)), static_cast<uint16_t>(discriminatorVal),
static_cast<uint32_t>(passcodeVal), resultCallback);

if (errorCode != CHIP_NO_ERROR) {
dispatch_async(queue, ^{
completion(nil, [MTRError errorForCHIPErrorCode:errorCode]);
});
return;
}

// resultCallback will handle things now.
}
errorHandler:^(NSError * error) {
dispatch_async(queue, ^{
completion(nil, error);
});
}];
}

#ifdef DEBUG
// This method is for unit testing only
- (void)failSubscribers:(dispatch_queue_t)clientQueue completion:(void (^)(void))completion
Expand Down
22 changes: 21 additions & 1 deletion src/darwin/Framework/CHIP/MTRDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ typedef NS_ENUM(NSUInteger, MTRDeviceState) {
* The current state of the device.
*
* The three states:
* MTRDeviceStateUnknkown
* MTRDeviceStateUnknown
* Unable to determine the state of the device at the moment.
*
* MTRDeviceStateReachable
Expand Down Expand Up @@ -137,6 +137,26 @@ typedef NS_ENUM(NSUInteger, MTRDeviceState) {
clientQueue:(dispatch_queue_t)clientQueue
completion:(MTRDeviceResponseHandler)completion;

/**
* Open a commissioning window on the device.
*
* On success, completion will be called on queue with the MTRSetupPayload that
* can be used to commission the device.
*
* @param setupPasscode The setup passcode to use for the commissioning window.
* See MTRSetupPayload's generateRandomSetupPasscode for
* generating a valid random passcode.
* @param discriminator The discriminator to use for the commissionable
* advertisement.
* @param duration Duration, in seconds, during which the commissioning
* window will be open.
*/
- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode
discriminator:(NSNumber *)discriminator
duration:(NSNumber *)duration
queue:(dispatch_queue_t)queue
completion:(MTRDeviceOpenCommissioningWindowHandler)completion;

@end

@protocol MTRDeviceDelegate <NSObject>
Expand Down
14 changes: 14 additions & 0 deletions src/darwin/Framework/CHIP/MTRDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,20 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
[self setExpectedValues:expectedValues expectedValueInterval:expectedValueInterval];
}

- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode
discriminator:(NSNumber *)discriminator
duration:(NSNumber *)duration
queue:(dispatch_queue_t)queue
completion:(MTRDeviceOpenCommissioningWindowHandler)completion
{
auto * baseDevice = [[MTRBaseDevice alloc] initWithNodeID:self.nodeID controller:self.deviceController];
[baseDevice openCommissioningWindowWithSetupPasscode:setupPasscode
discriminator:discriminator
duration:duration
queue:queue
completion:completion];
}

#pragma mark - Cache management

// assume lock is held
Expand Down
22 changes: 22 additions & 0 deletions src/darwin/Framework/CHIP/MTRDeviceController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,28 @@ - (BOOL)getSessionForCommissioneeDevice:(chip::NodeId)deviceID completion:(MTRIn
return YES;
}

- (void)asyncDispatchToMatterQueue:(void (^)(chip::Controller::DeviceCommissioner *))block
errorHandler:(void (^)(NSError *))errorHandler
{
{
NSError * error;
if (![self checkIsRunning:&error]) {
errorHandler(error);
return;
}
}

dispatch_async(_chipWorkQueue, ^{
NSError * error;
if (![self checkIsRunning:&error]) {
errorHandler(error);
return;
}

block(self.cppCommissioner);
});
}

@end

@implementation MTRDeviceController (InternalMethods)
Expand Down
Loading

0 comments on commit 2c9cd92

Please sign in to comment.