diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.h b/src/darwin/Framework/CHIP/MTRDeviceController.h index 2662d4b54e0f6d..112bd34c90c21a 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController.h @@ -178,16 +178,8 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) error:(NSError * __autoreleasing *)error MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); -/** - * Optionally pre-warm the controller for setting up a commissioning session. - * This may be called before setupCommissioningSessionWithPayload if it's known - * that a commissioning attempt will soon take place but the commissioning - * payload is not known yet. - * - * For example this may do a BLE scan in advance so results are ready earlier - * once the discriminator is known. - */ -- (void)preWarmCommissioningSession MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)); +- (void)preWarmCommissioningSession MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) + MTR_NEWLY_DEPRECATED("-[MTRDeviceControllerFactory preWarmCommissioningSession]"); /** * Set the Delegate for the device controller as well as the Queue on which the Delegate callbacks will be triggered diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index ec327a053962e6..94d9882b4cd9d1 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -86,7 +86,6 @@ static NSString * const kErrorPartialDacVerifierInit = @"Init failure while creating a partial DAC verifier"; static NSString * const kErrorPairDevice = @"Failure while pairing the device"; static NSString * const kErrorStopPairing = @"Failure while trying to stop the pairing process"; -static NSString * const kErrorPreWarmCommissioning = @"Failure while trying to pre-warm the commissioning process"; static NSString * const kErrorOpenPairingWindow = @"Open Pairing Window failed"; static NSString * const kErrorNotRunning = @"Controller is not running. Call startup first."; static NSString * const kErrorSetupCodeGen = @"Generating Manual Pairing Code failed"; @@ -896,15 +895,7 @@ - (BOOL)stopBrowseForCommissionables - (void)preWarmCommissioningSession { - auto block = ^{ - auto errorCode = chip::DeviceLayer::PlatformMgrImpl().PrepareCommissioning(); - MATTER_LOG_METRIC(kMetricPreWarmCommissioning, errorCode); - - // The checkForError is just so it logs - [MTRDeviceController checkForError:errorCode logMsg:kErrorPreWarmCommissioning error:nil]; - }; - - [self syncRunOnWorkQueue:block error:nil]; + [_factory preWarmCommissioningSession]; } - (MTRBaseDevice *)deviceBeingCommissionedWithNodeID:(NSNumber *)nodeID error:(NSError * __autoreleasing *)error diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.h b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.h index 62080b305ee635..5965d8bcaaf59b 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.h +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.h @@ -177,6 +177,18 @@ MTR_AVAILABLE(ios(16.4), macos(13.3), watchos(9.4), tvos(16.4)) - (MTRDeviceController * _Nullable)createControllerOnNewFabric:(MTRDeviceControllerStartupParams *)startupParams error:(NSError * __autoreleasing *)error; +/** + * If possible, pre-warm the Matter stack for setting up a commissioning session. + * + * This may be called before -[MTRDeviceController setupCommissioningSessionWithPayload:] + * if it is known that a commissioning attempt will soon take place, but the commissioning + * payload is not known yet. + * + * The controller factory must be running for pre-warming to take place. Pre-warming can take place + * before any controllers are started. + */ +- (void)preWarmCommissioningSession MTR_NEWLY_AVAILABLE; + @end /** diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm index 25939d6ae5c64e..c7d69d2c8772db 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm @@ -34,6 +34,7 @@ #import "MTRFabricInfo_Internal.h" #import "MTRFramework.h" #import "MTRLogging_Internal.h" +#import "MTRMetricKeys.h" #import "MTRMetricsCollector.h" #import "MTROTAProviderDelegateBridge.h" #import "MTROperationalBrowser.h" @@ -66,6 +67,7 @@ using namespace chip; using namespace chip::Controller; +using namespace chip::Tracing::DarwinFramework; static bool sExitHandlerRegistered = false; static void ShutdownOnExit() @@ -82,6 +84,11 @@ @interface MTRDeviceControllerFactoryParams () @end +MTR_DIRECT_MEMBERS +@interface MTRDeviceControllerFactory () +- (void)preWarmCommissioningSessionDone; +@end + MTR_DIRECT_MEMBERS @implementation MTRDeviceControllerFactory { dispatch_queue_t _chipWorkQueue; @@ -163,6 +170,13 @@ @implementation MTRDeviceControllerFactory { // in an atomic way that endpoint IDs are unique. NSMutableArray * _serverEndpoints; os_unfair_lock _serverEndpointsLock; // Protects access to _serverEndpoints. + + class final : public DeviceLayer::BleScannerDelegate { + void OnBleScanStopped() override + { + [MTRDeviceControllerFactory.sharedInstance preWarmCommissioningSessionDone]; + } + } _preWarmingDelegate; } + (void)initialize @@ -742,6 +756,32 @@ - (MTRDeviceController * _Nullable)createControllerOnNewFabric:(MTRDeviceControl error:error]; } +- (void)preWarmCommissioningSession +{ + dispatch_async(_chipWorkQueue, ^{ + CHIP_ERROR err = CHIP_ERROR_INCORRECT_STATE; + if (!self->_running) { + MTR_LOG_ERROR("Can't pre-warm, Matter controller factory is not running"); + } else { + MTR_LOG_DEFAULT("Pre-warming commissioning session"); + self->_controllerFactory->EnsureAndRetainSystemState(); + err = DeviceLayer::PlatformMgrImpl().StartBleScan(&self->_preWarmingDelegate, DeviceLayer::BleScanMode::kPreWarm); + if (err != CHIP_NO_ERROR) { + MTR_LOG_ERROR("Pre-warming failed: %" CHIP_ERROR_FORMAT, err.Format()); + self->_controllerFactory->ReleaseSystemState(); + } + } + MATTER_LOG_METRIC(kMetricPreWarmCommissioning, err); + }); +} + +- (void)preWarmCommissioningSessionDone +{ + assertChipStackLockedByCurrentThread(); + MTR_LOG_DEFAULT("Pre-warming done"); + self->_controllerFactory->ReleaseSystemState(); +} + // Finds a fabric that matches the given params, if one exists. // // Returns NO on failure, YES on success. If YES is returned, the diff --git a/src/platform/Darwin/BLEManagerImpl.cpp b/src/platform/Darwin/BLEManagerImpl.cpp index c0af6ccf17d93e..8ff634c52cb1ed 100644 --- a/src/platform/Darwin/BLEManagerImpl.cpp +++ b/src/platform/Darwin/BLEManagerImpl.cpp @@ -87,24 +87,18 @@ void BLEManagerImpl::_Shutdown() } } -CHIP_ERROR BLEManagerImpl::StartScan(BleScannerDelegate * delegate) +CHIP_ERROR BLEManagerImpl::StartScan(BleScannerDelegate * delegate, BleScanMode mode) { - if (mConnectionDelegate) - { - static_cast(mConnectionDelegate)->StartScan(delegate); - return CHIP_NO_ERROR; - } - return CHIP_ERROR_INCORRECT_STATE; + VerifyOrReturnError(mConnectionDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE); + static_cast(mConnectionDelegate)->StartScan(delegate, mode); + return CHIP_NO_ERROR; } CHIP_ERROR BLEManagerImpl::StopScan() { - if (mConnectionDelegate) - { - static_cast(mConnectionDelegate)->StopScan(); - return CHIP_NO_ERROR; - } - return CHIP_ERROR_INCORRECT_STATE; + VerifyOrReturnError(mConnectionDelegate != nullptr, CHIP_ERROR_INCORRECT_STATE); + static_cast(mConnectionDelegate)->StopScan(); + return CHIP_NO_ERROR; } bool BLEManagerImpl::_IsAdvertisingEnabled() diff --git a/src/platform/Darwin/BLEManagerImpl.h b/src/platform/Darwin/BLEManagerImpl.h index 72856ff466c2ac..194dadb2e79529 100644 --- a/src/platform/Darwin/BLEManagerImpl.h +++ b/src/platform/Darwin/BLEManagerImpl.h @@ -25,14 +25,12 @@ #include #include +#include #if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE namespace chip { namespace DeviceLayer { - -class BleScannerDelegate; - namespace Internal { using namespace chip::Ble; @@ -48,7 +46,7 @@ class BLEManagerImpl final : public BLEManager, private BleLayer public: CHIP_ERROR ConfigureBle(uint32_t aNodeId, bool aIsCentral) { return CHIP_NO_ERROR; } - CHIP_ERROR StartScan(BleScannerDelegate * delegate = nullptr); + CHIP_ERROR StartScan(BleScannerDelegate * delegate, BleScanMode mode = BleScanMode::kDefault); CHIP_ERROR StopScan(); private: diff --git a/src/platform/Darwin/BleConnectionDelegate.h b/src/platform/Darwin/BleConnectionDelegate.h index 1441f60610c822..585624ae407dfa 100644 --- a/src/platform/Darwin/BleConnectionDelegate.h +++ b/src/platform/Darwin/BleConnectionDelegate.h @@ -18,6 +18,7 @@ #pragma once #include +#include namespace chip { namespace DeviceLayer { @@ -26,11 +27,12 @@ namespace Internal { class BleConnectionDelegateImpl : public Ble::BleConnectionDelegate { public: - void StartScan(BleScannerDelegate * delegate = nullptr); + void StartScan(BleScannerDelegate * delegate, BleScanMode mode = BleScanMode::kDefault); void StopScan(); - virtual void NewConnection(Ble::BleLayer * bleLayer, void * appState, const SetupDiscriminator & connDiscriminator); - virtual void NewConnection(Ble::BleLayer * bleLayer, void * appState, BLE_CONNECTION_OBJECT connObj); - virtual CHIP_ERROR CancelConnection(); + + void NewConnection(Ble::BleLayer * bleLayer, void * appState, const SetupDiscriminator & connDiscriminator) override; + void NewConnection(Ble::BleLayer * bleLayer, void * appState, BLE_CONNECTION_OBJECT connObj) override; + CHIP_ERROR CancelConnection() override; private: CHIP_ERROR DoCancel(); diff --git a/src/platform/Darwin/BleConnectionDelegateImpl.mm b/src/platform/Darwin/BleConnectionDelegateImpl.mm index b325d079ba61a5..5eb1c4a74aa082 100644 --- a/src/platform/Darwin/BleConnectionDelegateImpl.mm +++ b/src/platform/Darwin/BleConnectionDelegateImpl.mm @@ -38,18 +38,19 @@ #import "UUIDHelper.h" using namespace chip::Ble; +using namespace chip::DeviceLayer; using namespace chip::Tracing::DarwinPlatform; constexpr uint64_t kScanningWithDiscriminatorTimeoutInSeconds = 60; -constexpr uint64_t kScanningWithoutDelegateTimeoutInSeconds = 120; +constexpr uint64_t kPreWarmScanTimeoutInSeconds = 120; constexpr uint64_t kCachePeripheralTimeoutInSeconds = static_cast(CHIP_DEVICE_CONFIG_BLE_SLOW_ADVERTISING_INTERVAL_MAX / 1000.0 * 8.0 * 0.625); constexpr char kBleWorkQueueName[] = "org.csa-iot.matter.framework.ble.workqueue"; typedef NS_ENUM(uint8_t, BleConnectionMode) { kUndefined = 0, - kScanningWithoutDelegate, kScanning, + kScanningWithTimeout, kConnecting, }; @@ -72,16 +73,14 @@ @interface BleConnection : NSObject OnBleScanStopped(); + }); + } + return; + } + // If the previous connection delegate was not a try to connect to something, just reuse it instead of // creating a brand new connection but update the discriminator and the ble layer members. if (ble and ![ble isConnecting]) { - [ble updateWithDelegate:delegate]; + [ble updateWithDelegate:delegate prewarm:prewarm]; return; } [ble stop]; - ble = [[BleConnection alloc] initWithDelegate:delegate queue:bleWorkQueue]; + ble = [[BleConnection alloc] initWithDelegate:delegate prewarm:prewarm queue:bleWorkQueue]; // Do _not_ set onConnectionComplete and onConnectionError // here. The connection callbacks we have expect an appState // that we do not have here, and in any case connection @@ -245,14 +258,16 @@ - (id)initWithQueue:(dispatch_queue_t)queue return self; } -- (id)initWithDelegate:(chip::DeviceLayer::BleScannerDelegate *)delegate queue:(dispatch_queue_t)queue +- (id)initWithDelegate:(chip::DeviceLayer::BleScannerDelegate *)delegate prewarm:(bool)prewarm queue:(dispatch_queue_t)queue { self = [self initWithQueue:queue]; if (self) { _scannerDelegate = delegate; - _currentMode = (delegate == nullptr) ? kScanningWithoutDelegate : kScanning; - if (_currentMode == kScanningWithoutDelegate) { - [self setupTimer:kScanningWithoutDelegateTimeoutInSeconds]; + if (prewarm) { + _currentMode = kScanningWithTimeout; + [self setupTimer:kPreWarmScanTimeoutInSeconds]; + } else { + _currentMode = kScanning; } } @@ -271,16 +286,6 @@ - (id)initWithDiscriminator:(const chip::SetupDiscriminator &)deviceDiscriminato return self; } -- (BOOL)isScanningWithoutDelegate -{ - return _currentMode == kScanningWithoutDelegate; -} - -- (BOOL)isScanning -{ - return _currentMode == kScanning; -} - - (BOOL)isConnecting { return _currentMode == kConnecting; @@ -592,7 +597,7 @@ - (void)start - (void)stop { - _scannerDelegate = nil; + [self detachScannerDelegate]; _found = false; [self stopScanning]; [self removePeripheralsFromCache]; @@ -669,31 +674,46 @@ - (void)connect:(CBPeripheral *)peripheral [_centralManager connectPeripheral:peripheral options:nil]; } -- (void)updateWithDelegate:(chip::DeviceLayer::BleScannerDelegate *)delegate +- (void)detachScannerDelegate { - _scannerDelegate = delegate; - _currentMode = (delegate == nullptr) ? kScanningWithoutDelegate : kScanning; + auto * existingDelegate = _scannerDelegate; + if (existingDelegate) { + _scannerDelegate = nullptr; + dispatch_async(_chipWorkQueue, ^{ + existingDelegate->OnBleScanStopped(); + }); + } +} - if (_currentMode == kScanning) { - [self clearTimer]; +- (void)updateWithDelegate:(chip::DeviceLayer::BleScannerDelegate *)delegate prewarm:(bool)prewarm +{ + [self detachScannerDelegate]; + if (delegate) { for (CBPeripheral * cachedPeripheral in _cachedPeripherals) { NSData * serviceData = _cachedPeripherals[cachedPeripheral][@"data"]; dispatch_async(_chipWorkQueue, ^{ ChipBLEDeviceIdentificationInfo info; memcpy(&info, [serviceData bytes], sizeof(info)); - _scannerDelegate->OnBleScanAdd((__bridge void *) cachedPeripheral, info); + delegate->OnBleScanAdd((__bridge void *) cachedPeripheral, info); }); } + _scannerDelegate = delegate; + } + + if (prewarm) { + _currentMode = kScanningWithTimeout; + [self setupTimer:kPreWarmScanTimeoutInSeconds]; } else { - [self setupTimer:kScanningWithoutDelegateTimeoutInSeconds]; + _currentMode = kScanning; + [self clearTimer]; } } - (void)updateWithDiscriminator:(const chip::SetupDiscriminator &)deviceDiscriminator { + [self detachScannerDelegate]; _deviceDiscriminator = deviceDiscriminator; - _scannerDelegate = nil; _currentMode = kConnecting; CBPeripheral * peripheral = nil; @@ -722,7 +742,7 @@ - (void)updateWithDiscriminator:(const chip::SetupDiscriminator &)deviceDiscrimi - (void)updateWithPeripheral:(CBPeripheral *)peripheral { - _scannerDelegate = nil; + [self detachScannerDelegate]; _currentMode = kConnecting; MATTER_LOG_METRIC_BEGIN(kMetricBLEDiscoveredMatchingPeripheral); diff --git a/src/platform/Darwin/BleScannerDelegate.h b/src/platform/Darwin/BleScannerDelegate.h index 9c96445145d67f..177bf259fd3f61 100644 --- a/src/platform/Darwin/BleScannerDelegate.h +++ b/src/platform/Darwin/BleScannerDelegate.h @@ -24,16 +24,29 @@ namespace chip { namespace DeviceLayer { +enum class BleScanMode +{ + /// Scan continues until stopped. + /// Replaces existing BLE operation. + kDefault = 0, + /// Scan stops automatically after some time. + /// Does not replace an existing BLE operation. + kPreWarm, +}; + class DLL_EXPORT BleScannerDelegate { public: virtual ~BleScannerDelegate() {} // Called when a scan result is available. - virtual void OnBleScanAdd(BLE_CONNECTION_OBJECT connObj, const Ble::ChipBLEDeviceIdentificationInfo & info) = 0; + virtual void OnBleScanAdd(BLE_CONNECTION_OBJECT connObj, const Ble::ChipBLEDeviceIdentificationInfo & info) {} // Called when a scan result is not available anymore. - virtual void OnBleScanRemove(BLE_CONNECTION_OBJECT connObj) = 0; + virtual void OnBleScanRemove(BLE_CONNECTION_OBJECT connObj) {} + + // Called when the scan has been stopped + virtual void OnBleScanStopped() {} }; } // namespace DeviceLayer diff --git a/src/platform/Darwin/PlatformManagerImpl.cpp b/src/platform/Darwin/PlatformManagerImpl.cpp index b7dc8b681fa8e8..d1dc5308c869e9 100644 --- a/src/platform/Darwin/PlatformManagerImpl.cpp +++ b/src/platform/Darwin/PlatformManagerImpl.cpp @@ -156,28 +156,24 @@ bool PlatformManagerImpl::IsWorkQueueCurrentQueue() const return dispatch_get_specific(this) == this; } -CHIP_ERROR PlatformManagerImpl::StartBleScan(BleScannerDelegate * delegate) +CHIP_ERROR PlatformManagerImpl::StartBleScan(BleScannerDelegate * delegate, BleScanMode mode) { #if CONFIG_NETWORK_LAYER_BLE - ReturnErrorOnFailureWithMetric(kMetricBLEScan, Internal::BLEMgrImpl().StartScan(delegate)); -#endif // CONFIG_NETWORK_LAYER_BLE + ReturnErrorOnFailureWithMetric(kMetricBLEScan, Internal::BLEMgrImpl().StartScan(delegate, mode)); return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // CONFIG_NETWORK_LAYER_BLE } CHIP_ERROR PlatformManagerImpl::StopBleScan() { #if CONFIG_NETWORK_LAYER_BLE ReturnErrorOnFailureWithMetric(kMetricBLEScan, Internal::BLEMgrImpl().StopScan()); -#endif // CONFIG_NETWORK_LAYER_BLE return CHIP_NO_ERROR; -} - -CHIP_ERROR PlatformManagerImpl::PrepareCommissioning() -{ -#if CONFIG_NETWORK_LAYER_BLE - ReturnErrorOnFailureWithMetric(kMetricBLEStartPreWarmScan, Internal::BLEMgrImpl().StartScan()); +#else + return CHIP_ERROR_NOT_IMPLEMENTED; #endif // CONFIG_NETWORK_LAYER_BLE - return CHIP_NO_ERROR; } } // namespace DeviceLayer diff --git a/src/platform/Darwin/PlatformManagerImpl.h b/src/platform/Darwin/PlatformManagerImpl.h index 12b515ae83d42f..ba37badf4628b5 100644 --- a/src/platform/Darwin/PlatformManagerImpl.h +++ b/src/platform/Darwin/PlatformManagerImpl.h @@ -24,6 +24,7 @@ #pragma once #include +#include #include #include @@ -49,9 +50,8 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener dispatch_queue_t GetWorkQueue() { return mWorkQueue; } bool IsWorkQueueCurrentQueue() const; - CHIP_ERROR StartBleScan(BleScannerDelegate * delegate = nullptr); + CHIP_ERROR StartBleScan(BleScannerDelegate * delegate, BleScanMode mode = BleScanMode::kDefault); CHIP_ERROR StopBleScan(); - CHIP_ERROR PrepareCommissioning(); System::Clock::Timestamp GetStartTime() { return mStartTime; }