Skip to content

Commit

Permalink
[Darwin / BLE] Add some code to scan without a discriminator in order…
Browse files Browse the repository at this point in the history
… to prewarm for commissioning (#24033)

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

* [Darwin] Add an API to prepare the stack for commissioning

* [darwin-framework-tool] Add a prepare-commissioning command
  • Loading branch information
vivien-apple authored and pull[bot] committed Jan 19, 2024
1 parent 82f70a1 commit 1583856
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 9 deletions.
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 @@ -493,6 +494,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

0 comments on commit 1583856

Please sign in to comment.