using System; using System.Collections.Generic; using System.Threading; namespace CoreBluetooth { /// /// An interface that provides updates for the discovery and management of peripheral devices. /// https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate /// public interface ICBCentralManagerDelegate { void DidConnectPeripheral(CBCentralManager central, CBPeripheral peripheral) { } void DidDisconnectPeripheral(CBCentralManager central, CBPeripheral peripheral, CBError error) { } void DidFailToConnectPeripheral(CBCentralManager central, CBPeripheral peripheral, CBError error) { } void DidDiscoverPeripheral(CBCentralManager central, CBPeripheral peripheral, int rssi) { } void DidUpdateState(CBCentralManager central); } internal interface INativeCentralManagerDelegate { void DidConnect(string peripheralId) { } void DidDisconnectPeripheral(string peripheralId, CBError error) { } void DidFailToConnect(string peripheralId, CBError error) { } void DidDiscoverPeripheral(SafeNativePeripheralHandle peripheral, int rssi) { } void DidUpdateState(CBManagerState state) { } } /// /// An object that scans for, discovers, connects to, and manages peripherals. /// https://developer.apple.com/documentation/corebluetooth/cbcentralmanager /// public class CBCentralManager : CBManager, INativeCentralManagerDelegate, IDisposable { bool _disposed = false; SafeNativeCentralManagerHandle _handle; // key: peripheralId Dictionary _peripherals = new Dictionary(); ICBCentralManagerDelegate _delegate; public ICBCentralManagerDelegate Delegate { get => _delegate; set { ExceptionUtils.ThrowObjectDisposedExceptionIf(_disposed, this); _delegate = value; } } /// /// ICBCentralManagerDelegate callbacks will be called in this context. /// public SynchronizationContext CallbackContext { get; set; } NativeCentralManagerProxy _nativeCentralManagerProxy; public CBCentralManager(ICBCentralManagerDelegate centralDelegate = null, CBCentralManagerInitOptions options = null) { if (options == null) { _handle = SafeNativeCentralManagerHandle.Create(); } else { using var optionsDict = options.ToNativeDictionary(); _handle = SafeNativeCentralManagerHandle.Create(optionsDict.Handle); } Delegate = centralDelegate; _nativeCentralManagerProxy = new NativeCentralManagerProxy(_handle, this); CallbackContext = SynchronizationContext.Current; } public void Connect(CBPeripheral peripheral) { ExceptionUtils.ThrowObjectDisposedExceptionIf(_disposed, this); _nativeCentralManagerProxy.Connect(peripheral); } public void CancelPeripheralConnection(CBPeripheral peripheral) { ExceptionUtils.ThrowObjectDisposedExceptionIf(_disposed, this); _nativeCentralManagerProxy.CancelPeripheralConnection(peripheral); } public CBPeripheral[] RetrievePeripherals(params string[] peripheralIds) { ExceptionUtils.ThrowObjectDisposedExceptionIf(_disposed, this); var peripheralHandles = _nativeCentralManagerProxy.RetrievePeripherals(peripheralIds); var result = new CBPeripheral[peripheralHandles.Length]; for (var i = 0; i < peripheralHandles.Length; i++) { var peripheral = new CBPeripheral(peripheralHandles[i], CallbackContext); SetPeripheral(peripheral); result[i] = peripheral; } return result; } public void ScanForPeripherals(string[] serviceUUIDs = null) { ExceptionUtils.ThrowObjectDisposedExceptionIf(_disposed, this); _nativeCentralManagerProxy.ScanForPeripherals(serviceUUIDs); } public void StopScan() { ExceptionUtils.ThrowObjectDisposedExceptionIf(_disposed, this); _nativeCentralManagerProxy.StopScan(); } public bool IsScanning { get { ExceptionUtils.ThrowObjectDisposedExceptionIf(_disposed, this); return _nativeCentralManagerProxy.IsScanning(); } } CBPeripheral GetPeripheral(string peripheralId) { if (!_peripherals.TryGetValue(peripheralId, out var peripheral)) { UnityEngine.Debug.LogError("Peripheral not found."); return null; } return peripheral; } void SetPeripheral(CBPeripheral peripheral) { if (_peripherals.ContainsKey(peripheral.Identifier)) { _peripherals[peripheral.Identifier].Dispose(); } _peripherals[peripheral.Identifier] = peripheral; } void INativeCentralManagerDelegate.DidConnect(string peripheralId) { CallbackContext.Post(_ => { if (_disposed) return; var peripheral = GetPeripheral(peripheralId); if (peripheral == null) return; Delegate?.DidConnectPeripheral(this, peripheral); }, null); } void INativeCentralManagerDelegate.DidDisconnectPeripheral(string peripheralId, CBError error) { CallbackContext.Post(_ => { if (_disposed) return; var peripheral = GetPeripheral(peripheralId); if (peripheral == null) return; Delegate?.DidDisconnectPeripheral(this, peripheral, error); }, null); } void INativeCentralManagerDelegate.DidFailToConnect(string peripheralId, CBError error) { CallbackContext.Post(_ => { if (_disposed) return; var peripheral = GetPeripheral(peripheralId); if (peripheral == null) return; Delegate?.DidFailToConnectPeripheral(this, peripheral, error); }, null); } void INativeCentralManagerDelegate.DidDiscoverPeripheral(SafeNativePeripheralHandle peripheralHandle, int rssi) { CallbackContext.Post(_ => { if (_disposed) return; var peripheral = new CBPeripheral(peripheralHandle, CallbackContext); SetPeripheral(peripheral); Delegate?.DidDiscoverPeripheral(this, peripheral, rssi); }, null); } void INativeCentralManagerDelegate.DidUpdateState(CBManagerState state) { CallbackContext.Post(_ => { if (_disposed) return; this.State = state; Delegate?.DidUpdateState(this); }, null); } public void Dispose() { if (_disposed) return; _nativeCentralManagerProxy?.Dispose(); _handle?.Dispose(); foreach (var peripheral in _peripherals.Values) { peripheral.Dispose(); } _disposed = true; } } }