Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Darwin / BLE] Add some code to scan without a discriminator in order to prewarm for commissioning #24033

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/darwin-framework-tool/commands/pairing/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include "OpenCommissioningWindowCommand.h"
#include "PairingCommandBridge.h"
#include "PrepareCommissioningCommand.h"

class PairCode : public PairingCommandBridge
{
Expand Down Expand Up @@ -71,6 +72,7 @@ void registerCommandsPairing(Commands & commands)
make_unique<PairBleThread>(),
make_unique<Unpair>(),
make_unique<OpenCommissioningWindowCommand>(),
make_unique<PrepareCommissioningCommand>(),
};

commands.Register(clusterName, clusterCommands);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2022 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#import <Matter/Matter.h>

#include "../common/CHIPCommandBridge.h"

#import "MTRError_Utils.h"

class PrepareCommissioningCommand : public CHIPCommandBridge {
public:
PrepareCommissioningCommand()
: CHIPCommandBridge("prepare-commissioning")
{
}

protected:
/////////// CHIPCommandBridge Interface /////////
CHIP_ERROR RunCommand() override
{
auto * controller = CurrentCommissioner();
NSError * error;
if (![controller prepareCommissioningSession:&error]) {
auto err = MTRErrorToCHIPErrorCode(error);
SetCommandExitStatus(err);
return err;
}

// In interactive mode, we don't want to block the UI until the end of `GetWaitDuration`. So returns early.
if (IsInteractive()) {
SetCommandExitStatus(CHIP_NO_ERROR);
}
return CHIP_NO_ERROR;
}

chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(30); }
};
10 changes: 10 additions & 0 deletions src/darwin/Framework/CHIP/MTRDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@ typedef void (^MTRDeviceConnectionCallback)(MTRBaseDevice * _Nullable device, NS
- (nullable MTRBaseDevice *)deviceBeingCommissionedWithNodeID:(NSNumber *)nodeID
error:(NSError * __autoreleasing *)error MTR_NEWLY_AVAILABLE;

/**
* Prepare the controller for setting up a commissioning session.
*
* This method is intended to be used when it is known that a setting up a commissioning session
* will happen soon.
* For example it may ask different subsystems to look for useful informations onto the network
* ahead of commissioning that may then be re-used during commissioning.
*/
- (BOOL)prepareCommissioningSession:(NSError * __autoreleasing *)error MTR_NEWLY_AVAILABLE;

/**
* Controllers are created via the MTRDeviceControllerFactory object.
*/
Expand Down
14 changes: 14 additions & 0 deletions src/darwin/Framework/CHIP/MTRDeviceController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
static NSString * const kErrorPairDevice = @"Failure while pairing the device";
static NSString * const kErrorUnpairDevice = @"Failure while unpairing the device";
static NSString * const kErrorStopPairing = @"Failure while trying to stop the pairing process";
static NSString * const kErrorPrepareCommissioning = @"Failure while trying to prepare the commissioning process";
static NSString * const kErrorOpenPairingWindow = @"Open Pairing Window failed";
static NSString * const kErrorGetPairedDevice = @"Failure while trying to retrieve a paired device";
static NSString * const kErrorNotRunning = @"Controller is not running. Call startup first.";
Expand Down Expand Up @@ -488,6 +489,19 @@ - (BOOL)cancelCommissioningForNodeID:(NSNumber *)nodeID error:(NSError * __autor
return success;
}

- (BOOL)prepareCommissioningSession:(NSError * __autoreleasing *)error
{
__block BOOL success = NO;
dispatch_sync(_chipWorkQueue, ^{
VerifyOrReturn([self checkIsRunning:error]);

auto errorCode = chip::DeviceLayer::PlatformMgrImpl().PrepareCommissioning();
success = ![MTRDeviceController checkForError:errorCode logMsg:kErrorPrepareCommissioning error:error];
});

return success;
}

- (MTRBaseDevice *)deviceBeingCommissionedWithNodeID:(NSNumber *)nodeID error:(NSError * __autoreleasing *)error
{
VerifyOrReturnValue([self checkIsRunning:error], nil);
Expand Down
10 changes: 10 additions & 0 deletions src/platform/Darwin/BLEManagerImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ void BLEManagerImpl::_Shutdown()
}
}

CHIP_ERROR BLEManagerImpl::PrepareConnection()
{
if (mConnectionDelegate)
{
static_cast<BleConnectionDelegateImpl *>(mConnectionDelegate)->PrepareConnection();
return CHIP_NO_ERROR;
}
return CHIP_ERROR_INCORRECT_STATE;
}

bool BLEManagerImpl::_IsAdvertisingEnabled()
{
ChipLogDetail(DeviceLayer, "%s", __FUNCTION__);
Expand Down
1 change: 1 addition & 0 deletions src/platform/Darwin/BLEManagerImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class BLEManagerImpl final : public BLEManager, private BleLayer

public:
CHIP_ERROR ConfigureBle(uint32_t aNodeId, bool aIsCentral) { return CHIP_NO_ERROR; }
CHIP_ERROR PrepareConnection();

private:
// ===== Members that implement the BLEManager internal interface.
Expand Down
1 change: 1 addition & 0 deletions src/platform/Darwin/BleConnectionDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace Internal {
class BleConnectionDelegateImpl : public Ble::BleConnectionDelegate
{
public:
void PrepareConnection();
virtual void NewConnection(Ble::BleLayer * bleLayer, void * appState, const SetupDiscriminator & connDiscriminator);
virtual CHIP_ERROR CancelConnection();
};
Expand Down
126 changes: 117 additions & 9 deletions src/platform/Darwin/BleConnectionDelegateImpl.mm
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@

using namespace chip::Ble;

constexpr uint64_t kScanningTimeoutInSeconds = 60;
constexpr uint64_t kScanningWithDiscriminatorTimeoutInSeconds = 60;
constexpr uint64_t kScanningWithoutDiscriminatorTimeoutInSeconds = 120;

@interface BleConnection : NSObject <CBCentralManagerDelegate, CBPeripheralDelegate>

Expand All @@ -48,6 +49,8 @@ @interface BleConnection : NSObject <CBCentralManagerDelegate, CBPeripheralDeleg
@property (strong, nonatomic) CBPeripheral * peripheral;
@property (strong, nonatomic) CBUUID * shortServiceUUID;
@property (nonatomic, readonly, nullable) dispatch_source_t timer;
@property (strong, nonatomic) NSMutableDictionary * cachedPeripherals;
@property (unsafe_unretained, nonatomic) bool hasDeviceDiscriminator;
@property (unsafe_unretained, nonatomic) bool found;
@property (unsafe_unretained, nonatomic) chip::SetupDiscriminator deviceDiscriminator;
@property (unsafe_unretained, nonatomic) void * appState;
Expand All @@ -59,6 +62,9 @@ - (id)initWithDiscriminator:(const chip::SetupDiscriminator &)deviceDiscriminato
- (void)setBleLayer:(chip::Ble::BleLayer *)bleLayer;
- (void)start;
- (void)stop;
- (BOOL)hasDiscriminator;
- (void)updateWithDiscriminator:(const chip::SetupDiscriminator &)deviceDiscriminator;
- (void)update;

@end

Expand All @@ -71,6 +77,18 @@ - (void)stop;
Ble::BleLayer * bleLayer, void * appState, const SetupDiscriminator & deviceDiscriminator)
{
ChipLogProgress(Ble, "%s", __FUNCTION__);

// If the previous connection delegate was a scan without a discriminator, just reuse it instead of
// creating a brand new connection but update the discriminator and the ble layer members.
if (ble and ![ble hasDiscriminator]) {
[ble setBleLayer:bleLayer];
ble.appState = appState;
ble.onConnectionComplete = OnConnectionComplete;
ble.onConnectionError = OnConnectionError;
[ble updateWithDiscriminator:deviceDiscriminator];
return;
}

CancelConnection();
ble = [[BleConnection alloc] initWithDiscriminator:deviceDiscriminator];
[ble setBleLayer:bleLayer];
Expand All @@ -80,6 +98,24 @@ - (void)stop;
ble.centralManager = [ble.centralManager initWithDelegate:ble queue:ble.workQueue];
}

void BleConnectionDelegateImpl::PrepareConnection()
{
ChipLogProgress(Ble, "%s", __FUNCTION__);

// If the previous connection delegate was a scan without a discriminator, just reuse it instead of
// creating a brand new connection but clear the cache and reset the timer.
if (ble and ![ble hasDiscriminator]) {
[ble update];
return;
}

CancelConnection();
ble = [[BleConnection alloc] init];
ble.onConnectionComplete = OnConnectionComplete;
ble.onConnectionError = OnConnectionError;
ble.centralManager = [ble.centralManager initWithDelegate:ble queue:ble.workQueue];
}

CHIP_ERROR BleConnectionDelegateImpl::CancelConnection()
{
ChipLogProgress(Ble, "%s", __FUNCTION__);
Expand All @@ -98,39 +134,67 @@ @interface BleConnection ()

@implementation BleConnection

- (id)initWithDiscriminator:(const chip::SetupDiscriminator &)deviceDiscriminator
- (id)init
{
self = [super init];
if (self) {
self.shortServiceUUID = [UUIDHelper GetShortestServiceUUID:&chip::Ble::CHIP_BLE_SVC_ID];
_deviceDiscriminator = deviceDiscriminator;
_workQueue = dispatch_queue_create("com.chip.ble.work_queue", DISPATCH_QUEUE_SERIAL);
_chipWorkQueue = chip::DeviceLayer::PlatformMgrImpl().GetWorkQueue();
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _workQueue);
_centralManager = [CBCentralManager alloc];
_found = false;
_cachedPeripherals = [[NSMutableDictionary alloc] init];
_hasDeviceDiscriminator = false;

dispatch_source_set_event_handler(_timer, ^{
[self stop];
[self dispatchConnectionError:BLE_ERROR_APP_CLOSED_CONNECTION];
});
dispatch_source_set_timer(
_timer, dispatch_walltime(nullptr, kScanningTimeoutInSeconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 5 * NSEC_PER_SEC);

[self resetTimer];
}

return self;
}

- (id)initWithDiscriminator:(const chip::SetupDiscriminator &)deviceDiscriminator
{
self = [self init];
if (self) {
_deviceDiscriminator = deviceDiscriminator;
_hasDeviceDiscriminator = true;
[self resetTimer];
}

return self;
}

- (void)resetTimer
{
auto timeout =
[self hasDiscriminator] ? kScanningWithDiscriminatorTimeoutInSeconds : kScanningWithoutDiscriminatorTimeoutInSeconds;
dispatch_source_set_timer(_timer, dispatch_walltime(nullptr, timeout * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 5 * NSEC_PER_SEC);
}

// All our callback dispatch must happen on _chipWorkQueue
- (void)dispatchConnectionError:(CHIP_ERROR)error
{
if (self.onConnectionError == nil) {
return;
}

dispatch_async(_chipWorkQueue, ^{
self.onConnectionError(self.appState, error);
});
}

- (void)dispatchConnectionComplete:(CBPeripheral *)peripheral
{
if (self.onConnectionComplete == nil) {
return;
}

dispatch_async(_chipWorkQueue, ^{
self.onConnectionComplete(self.appState, (__bridge void *) peripheral);
});
Expand Down Expand Up @@ -183,10 +247,15 @@ - (void)centralManager:(CBCentralManager *)central
uint8_t opCode = bytes[0];
uint16_t discriminator = (bytes[1] | (bytes[2] << 8)) & 0xfff;

if ((opCode == 0 || opCode == 1) && [self checkDiscriminator:discriminator]) {
ChipLogProgress(Ble, "Connecting to device with discriminator: %d", discriminator);
[self connect:peripheral];
[self stopScanning];
if (opCode == 0 || opCode == 1) {
if (![self hasDiscriminator]) {
ChipLogProgress(Ble, "Storing device %p with discriminator: %d", peripheral, discriminator);
_cachedPeripherals[@(discriminator)] = peripheral;
} else if ([self checkDiscriminator:discriminator]) {
ChipLogProgress(Ble, "Connecting to device %p with discriminator: %d", peripheral, discriminator);
[self connect:peripheral];
[self stopScanning];
}
}
}

Expand All @@ -196,6 +265,11 @@ - (void)centralManager:(CBCentralManager *)central
}
}

- (BOOL)hasDiscriminator
{
return _hasDeviceDiscriminator;
}

- (BOOL)checkDiscriminator:(uint16_t)discriminator
{
return _deviceDiscriminator.MatchesLongDiscriminator(discriminator);
Expand Down Expand Up @@ -343,6 +417,8 @@ - (void)stop
{
[self stopScanning];
[self disconnect];
[_cachedPeripherals removeAllObjects];
_cachedPeripherals = nil;
_centralManager.delegate = nil;
_centralManager = nil;
_peripheral = nil;
Expand Down Expand Up @@ -386,6 +462,38 @@ - (void)disconnect
_peripheral = nil;
}

- (void)update
{
[_cachedPeripherals removeAllObjects];
[self resetTimer];
}

- (void)updateWithDiscriminator:(const chip::SetupDiscriminator &)deviceDiscriminator
{
_deviceDiscriminator = deviceDiscriminator;
_hasDeviceDiscriminator = true;

CBPeripheral * peripheral = nil;
if (deviceDiscriminator.IsShortDiscriminator()) {
for (NSNumber * longDiscriminator in _cachedPeripherals) {
if ([self checkDiscriminator:[longDiscriminator unsignedShortValue]]) {
peripheral = _cachedPeripherals[longDiscriminator];
break;
}
}
} else {
peripheral = _cachedPeripherals[@(deviceDiscriminator.GetLongValue())];
}

if (peripheral) {
ChipLogProgress(Ble, "Connecting to cached device: %p", peripheral);
[self connect:peripheral];
[self stopScanning];
} else {
[self resetTimer];
}
}

/**
* private static method to copy service and characteristic UUIDs from CBCharacteristic to a pair of ChipBleUUID objects.
* this is used in calls into Chip layer to decouple it from CoreBluetooth
Expand Down
9 changes: 9 additions & 0 deletions src/platform/Darwin/PlatformManagerImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,5 +161,14 @@ bool PlatformManagerImpl::_IsChipStackLockedByCurrentThread() const
};
#endif

CHIP_ERROR PlatformManagerImpl::PrepareCommissioning()
{
auto error = CHIP_NO_ERROR;
#if CONFIG_NETWORK_LAYER_BLE
error = Internal::BLEMgrImpl().PrepareConnection();
#endif // CONFIG_NETWORK_LAYER_BLE
return error;
}

} // namespace DeviceLayer
} // namespace chip
2 changes: 2 additions & 0 deletions src/platform/Darwin/PlatformManagerImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener
return mWorkQueue;
}

CHIP_ERROR PrepareCommissioning();

System::Clock::Timestamp GetStartTime() { return mStartTime; }

private:
Expand Down