public override async Task <bool> UpdateRssiAsync() { if (_gatt == null || _gattCallback == null) { Trace.Message("You can't read the RSSI value for disconnected devices except on discovery on Android. Device is {0}", State); return(false); } return(await TaskBuilder.FromEvent <bool, EventHandler <RssiReadCallbackEventArgs>, EventHandler>( execute : () => _gatt.ReadRemoteRssi(), getCompleteHandler : (complete, reject) => ((sender, args) => { if (args.Error == null) { Trace.Message("Read RSSI for {0} {1}: {2}", Id, Name, args.Rssi); Rssi = args.Rssi; complete(true); } else { Trace.Message($"Failed to read RSSI for device {Id}-{Name}. {args.Error.Message}"); complete(false); } }), subscribeComplete : handler => _gattCallback.RemoteRssiRead += handler, unsubscribeComplete : handler => _gattCallback.RemoteRssiRead -= handler, getRejectHandler : reject => ((sender, args) => { reject(new Exception($"Device {Name} disconnected while updating rssi.")); }), subscribeReject : handler => _gattCallback.ConnectionInterrupted += handler, unsubscribeReject : handler => _gattCallback.ConnectionInterrupted -= handler)); }
protected override bool UpdateConnectionIntervalNative(ConnectionInterval interval) { if (_gatt == null || _gattCallback == null) { Trace.Message("You can't update a connection interval for disconnected devices. Device is {0}", State); return(false); } if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop) { Trace.Message($"Update connection interval paramter in this Android API level"); return(false); } try { // map to android gattConnectionPriorities // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#CONNECTION_PRIORITY_BALANCED return(_gatt.RequestConnectionPriority((GattConnectionPriority)(int)interval)); } catch (Exception ex) { throw new Exception($"Update Connection Interval fails with error. {ex.Message}"); } }
private void StartScanningNew(Guid[] serviceUuids) { var hasFilter = serviceUuids?.Any() ?? false; List <ScanFilter> scanFilters = null; if (hasFilter) { scanFilters = new List <ScanFilter>(); foreach (var serviceUuid in serviceUuids) { var sfb = new ScanFilter.Builder(); sfb.SetServiceUuid(ParcelUuid.FromString(serviceUuid.ToString())); scanFilters.Add(sfb.Build()); } } var ssb = new ScanSettings.Builder(); ssb.SetScanMode(ScanMode.ToNative()); //ssb.SetCallbackType(ScanCallbackType.AllMatches); if (_bluetoothAdapter.BluetoothLeScanner != null) { Trace.Message($"Adapter >=21: Starting a scan for devices. ScanMode: {ScanMode}"); if (hasFilter) { Trace.Message($"ScanFilters: {string.Join(", ", serviceUuids)}"); } _bluetoothAdapter.BluetoothLeScanner.StartScan(scanFilters, ssb.Build(), _api21ScanCallback); } else { Trace.Message("Adapter >= 21: Scan failed. Bluetooth is probably off"); } }
public static List<AdvertisementRecord> ParseScanRecord(byte[] scanRecord) { var records = new List<AdvertisementRecord>(); if (scanRecord == null) return records; int index = 0; while (index < scanRecord.Length) { byte length = scanRecord[index++]; //Done once we run out of records // 1 byte for type and length-1 bytes for data if (length == 0) break; int type = scanRecord[index]; //Done if our record isn't a valid type if (type == 0) break; if (!Enum.IsDefined(typeof(AdvertisementRecordType), type)) { Trace.Message("Advertisment record type not defined: {0}", type); break; } //data length is length -1 because type takes the first byte byte[] data = new byte[length - 1]; Array.Copy(scanRecord, index + 1, data, 0, length - 1); // don't forget that data is little endian so reverse // Supplement to Bluetooth Core Specification 1 // NOTE: all relevant devices are already little endian, so this is not necessary for any type except UUIDs //var record = new AdvertisementRecord((AdvertisementRecordType)type, data.Reverse().ToArray()); switch ((AdvertisementRecordType)type) { case AdvertisementRecordType.ServiceDataUuid32Bit: case AdvertisementRecordType.SsUuids128Bit: case AdvertisementRecordType.SsUuids16Bit: case AdvertisementRecordType.SsUuids32Bit: case AdvertisementRecordType.UuidCom32Bit: case AdvertisementRecordType.UuidsComplete128Bit: case AdvertisementRecordType.UuidsComplete16Bit: case AdvertisementRecordType.UuidsIncomple16Bit: case AdvertisementRecordType.UuidsIncomplete128Bit: Array.Reverse(data); break; } var record = new AdvertisementRecord((AdvertisementRecordType)type, data); Trace.Message(record.ToString()); records.Add(record); //Advance index += length; } return records; }
//Second step public void CloseGatt() { if (_gatt != null) { _gatt.Close(); _gatt = null; } else { Trace.Message("[Warning]: Can't close gatt after disconnect {0}. Gatt is null.", Name); } }
public override List <IDevice> GetSystemConnectedOrPairedDevices(Guid[] services = null) { if (services != null) { Trace.Message("Caution: GetSystemConnectedDevices does not take into account the 'services' parameter on Android."); } var connectedDevices = _bluetoothManager.GetConnectedDevices(ProfileType.Gatt).Where(d => d.Type == BluetoothDeviceType.Le); var bondedDevices = _bluetoothAdapter.BondedDevices.Where(d => d.Type == BluetoothDeviceType.Le); return(connectedDevices.Union(bondedDevices, new DeviceComparer()).Select(d => new Device(this, d, null, null, 0)).Cast <IDevice>().ToList()); }
// First step public void Disconnect() { if (_gatt != null) { ClearServices(); _gatt.Disconnect(); } else { Trace.Message("[Warning]: Can't disconnect {0}. Gatt is null.", Name); } }
private void StartScanningOld(Guid[] serviceUuids) { var hasFilter = serviceUuids?.Any() ?? false; UUID[] uuids = null; if (hasFilter) { uuids = serviceUuids.Select(u => UUID.FromString(u.ToString())).ToArray(); } Trace.Message("Adapter < 21: Starting a scan for devices."); #pragma warning disable 618 _bluetoothAdapter.StartLeScan(uuids, _api18ScanCallback); #pragma warning restore 618 }
/// <summary> /// This method is only called by a user triggered disconnect. /// A user will first trigger _gatt.disconnect -> which in turn will trigger _gatt.Close() via the gattCallback /// </summary> public void Disconnect() { if (_gatt != null) { IsOperationRequested = true; ClearServices(); _gatt.Disconnect(); } else { Trace.Message("[Warning]: Can't disconnect {0}. Gatt is null.", Name); } }
protected override void StopScanNative() { if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop) { Trace.Message("Adapter < 21: Stopping the scan for devices."); #pragma warning disable 618 _bluetoothAdapter.StopLeScan(_api18ScanCallback); #pragma warning restore 618 } else { Trace.Message("Adapter >= 21: Stopping the scan for devices."); _bluetoothAdapter.BluetoothLeScanner?.StopScan(_api21ScanCallback); } }
/// <summary> /// This method is only called by a user triggered disconnect. /// A user will first trigger _gatt.disconnect -> which in turn will trigger _gatt.Close() via the gattCallback /// </summary> public void Disconnect() { if (_gatt != null) { IsOperationRequested = true; IsAutoConnectRequested = false; // user disconnect disables autconnect ClearServices(); _gatt.Disconnect(); } else { Trace.Message("[Warning]: Can't disconnect {0}. Gatt is null.", Name); } }
public async Task <bool> WriteAsync(byte[] data) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (!CanWrite) { throw new InvalidOperationException("Characteristic does not support write."); } var writeType = GetWriteType(); Trace.Message("Characteristic.WriteAsync"); return(await WriteNativeAsync(data, writeType)); }
private void StartScanningNew(Guid[] serviceUuids, ManufacturerData[] manufacturerDataFilters) { var hasFilter = serviceUuids?.Any() ?? false; List <ScanFilter> scanFilters = null; if (hasFilter) { scanFilters = new List <ScanFilter>(); foreach (var serviceUuid in serviceUuids) { var sfb = new ScanFilter.Builder(); sfb.SetServiceUuid(ParcelUuid.FromString(serviceUuid.ToString())); scanFilters.Add(sfb.Build()); } foreach (var manufacturerData in manufacturerDataFilters) { var sfb = new ScanFilter.Builder(); sfb.SetManufacturerData(manufacturerData.Id, manufacturerData.Data); scanFilters.Add(sfb.Build()); } } var ssb = new ScanSettings.Builder(); ssb.SetScanMode(ScanMode.ToNative()); if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { // enable Bluetooth 5 Advertisement Extensions on Android 8.0 and above ssb.SetLegacy(false); } //ssb.SetCallbackType(ScanCallbackType.AllMatches); if (_bluetoothAdapter.BluetoothLeScanner != null) { Trace.Message($"Adapter >=21: Starting a scan for devices. ScanMode: {ScanMode}"); if (hasFilter) { Trace.Message($"ScanFilters: {string.Join(", ", serviceUuids)}"); } _bluetoothAdapter.BluetoothLeScanner.StartScan(scanFilters, ssb.Build(), _api21ScanCallback); } else { Trace.Message("Adapter >= 21: Scan failed. Bluetooth is probably off"); } }
public async Task ConnectToDeviceAsync(IDevice device, ConnectParameters connectParameters = default(ConnectParameters), CancellationToken cancellationToken = default(CancellationToken)) { if (device == null) { throw new ArgumentNullException(nameof(device)); } if (device.State == DeviceState.Connected) { return; } using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) { await TaskBuilder.FromEvent <bool, EventHandler <DeviceEventArgs>, EventHandler <DeviceErrorEventArgs> >( execute : () => { ConnectToDeviceNativeAsync(device, connectParameters, cts.Token); }, getCompleteHandler : (complete, reject) => (sender, args) => { if (args.Device.Id == device.Id) { Trace.Message("ConnectToDeviceAsync Connected: {0} {1}", args.Device.Id, args.Device.Name); complete(true); } }, subscribeComplete : handler => DeviceConnected += handler, unsubscribeComplete : handler => DeviceConnected -= handler, getRejectHandler : reject => (sender, args) => { if (args.Device?.Id == device.Id) { Trace.Message("ConnectAsync Error: {0} {1}", args.Device?.Id, args.Device?.Name); reject(new DeviceConnectionException((Guid)args.Device?.Id, args.Device?.Name, args.ErrorMessage)); } }, subscribeReject : handler => DeviceConnectionError += handler, unsubscribeReject : handler => DeviceConnectionError -= handler, token : cts.Token); } }
public void ClearServices() { this.CancelEverythingAndReInitialize(); foreach (var service in _knownServices) { try { service.Dispose(); } catch (Exception ex) { Trace.Message("Exception while cleanup of service: {0}", ex.Message); } } _knownServices.Clear(); }
/// <summary> /// CloseGatt is called by the gattCallback in case of user disconnect or a disconnect by signal loss or a connection error. /// Cleares all cached services. /// </summary> public void CloseGatt() { if (_gatt == null) { Trace.Message("[Warning]: Can't close gatt after disconnect {0}. Gatt is null.", Name); return; } // ClossGatt might will get called on signal loss without Disconnect being called // we have to make sure we clear the services ClearServices(); // signal loss will not determine the closing of our gatt instance so that autoconnect can work if (!IsAutoConnectRequested) { _gatt.Close(); _gatt = null; } }
public Task ConnectToDeviceAsync(IDevice device, bool autoconnect = false, CancellationToken cancellationToken = default(CancellationToken)) { if (device == null) { throw new ArgumentNullException(nameof(device)); } if (device.State == DeviceState.Connected) { return(Task.FromResult(true)); } return(TaskBuilder.FromEvent <bool, EventHandler <DeviceEventArgs>, EventHandler <DeviceErrorEventArgs> >( execute: () => { ConnectToDeviceNativeAsync(device, autoconnect, cancellationToken); }, getCompleteHandler: (complete, reject) => (sender, args) => { if (args.Device.Id == device.Id) { Trace.Message("ConnectToDeviceAsync Connected: {0} {1}", args.Device.Id, args.Device.Name); complete(true); } }, subscribeComplete: handler => DeviceConnected += handler, unsubscribeComplete: handler => DeviceConnected -= handler, getRejectHandler: reject => (sender, args) => { if (args.Device?.Id == device.Id) { Trace.Message("ConnectAsync Error: {0} {1}", args.Device?.Id, args.Device?.Name); reject(new DeviceConnectionException((Guid)args.Device?.Id, args.Device?.Name, args.ErrorMessage)); } }, subscribeReject: handler => DeviceConnectionError += handler, unsubscribeReject: handler => DeviceConnectionError -= handler, token: cancellationToken)); }
public void HandleDisconnectedDevice(bool disconnectRequested, IDevice device) { if (disconnectRequested) { Trace.Message("DisconnectedPeripheral by user: {0}", device.Name); DeviceDisconnected(this, new DeviceEventArgs { Device = device }); } else { Trace.Message("DisconnectedPeripheral by lost signal: {0}", device.Name); DeviceConnectionLost(this, new DeviceErrorEventArgs { Device = device }); if (DiscoveredDevices.Contains(device)) { DiscoveredDevices.Remove(device); } } }
protected override async Task <int> RequestMtuNativeAsync(int requestValue) { if (_gatt == null || _gattCallback == null) { Trace.Message("You can't request a MTU for disconnected devices. Device is {0}", State); return(-1); } if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop) { Trace.Message($"Request MTU not supported in this Android API level"); return(-1); } return(await TaskBuilder.FromEvent <int, EventHandler <MtuRequestCallbackEventArgs>, EventHandler>( execute : () => { _gatt.RequestMtu(requestValue); }, getCompleteHandler : (complete, reject) => ((sender, args) => { if (args.Error != null) { Trace.Message($"Failed to request MTU ({requestValue}) for device {Id}-{Name}. {args.Error.Message}"); reject(new Exception($"Request MTU error: {args.Error.Message}")); } else { complete(args.Mtu); } }), subscribeComplete : handler => _gattCallback.MtuRequested += handler, unsubscribeComplete : handler => _gattCallback.MtuRequested -= handler, getRejectHandler : reject => ((sender, args) => { reject(new Exception($"Device {Name} disconnected while requesting MTU.")); }), subscribeReject : handler => _gattCallback.ConnectionInterrupted += handler, unsubscribeReject : handler => _gattCallback.ConnectionInterrupted -= handler )); }
public override void OnScanFailed(ScanFailure errorCode) { Trace.Message("Adapter: Scan failed with code {0}", errorCode); base.OnScanFailed(errorCode); }
public void OnLeScan(BluetoothDevice bleDevice, int rssi, byte[] scanRecord) { Trace.Message("Adapter.LeScanCallback: " + bleDevice.Name); _adapter.HandleDiscoveredDevice(new Device(_adapter, bleDevice, null, rssi, scanRecord)); }
private void StartScanningNew(ScanFilterOptions scanFilterOptions) { var hasFilter = scanFilterOptions?.HasFilter == true; List <ScanFilter> scanFilters = null; if (hasFilter) { scanFilters = new List <ScanFilter>(); if (scanFilterOptions.HasServiceIds) { foreach (var serviceUuid in scanFilterOptions.ServiceUuids) { var sfb = new ScanFilter.Builder(); sfb.SetServiceUuid(ParcelUuid.FromString(serviceUuid.ToString())); scanFilters.Add(sfb.Build()); } } if (scanFilterOptions.HasServiceData) { foreach (var serviceDataFilter in scanFilterOptions.ServiceDataFilters) { var sfb = new ScanFilter.Builder(); if (serviceDataFilter.ServiceDataMask == null) { sfb.SetServiceData(ParcelUuid.FromString(serviceDataFilter.ServiceDataUuid.ToString()), serviceDataFilter.ServiceData); } else { sfb.SetServiceData(ParcelUuid.FromString(serviceDataFilter.ServiceDataUuid.ToString()), serviceDataFilter.ServiceData, serviceDataFilter.ServiceDataMask); } scanFilters.Add(sfb.Build()); } } if (scanFilterOptions.HasManufacturerIds) { foreach (var manufacturerDataFilter in scanFilterOptions.ManufacturerDataFilters) { var sfb = new ScanFilter.Builder(); if (manufacturerDataFilter.ManufacturerDataMask != null) { sfb.SetManufacturerData(manufacturerDataFilter.ManufacturerId, manufacturerDataFilter.ManufacturerData); } else { sfb.SetManufacturerData(manufacturerDataFilter.ManufacturerId, manufacturerDataFilter.ManufacturerData, manufacturerDataFilter.ManufacturerDataMask); } scanFilters.Add(sfb.Build()); } } if (scanFilterOptions.HasDeviceAddresses) { foreach (var deviceAddress in scanFilterOptions.DeviceAddresses) { if (BluetoothAdapter.CheckBluetoothAddress(deviceAddress)) { var sfb = new ScanFilter.Builder(); sfb.SetDeviceAddress(deviceAddress); scanFilters.Add(sfb.Build()); } else { Trace.Message($"Device address {deviceAddress} is invalid. The correct format is \"01:02:03:AB:CD:EF\""); } } } if (scanFilterOptions.HasDeviceNames) { foreach (var deviceName in scanFilterOptions.DeviceNames) { var sfb = new ScanFilter.Builder(); sfb.SetDeviceName(deviceName); scanFilters.Add(sfb.Build()); } } } var ssb = new ScanSettings.Builder(); ssb.SetScanMode(ScanMode.ToNative()); if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { // enable Bluetooth 5 Advertisement Extensions on Android 8.0 and above ssb.SetLegacy(false); } //ssb.SetCallbackType(ScanCallbackType.AllMatches); if (_bluetoothAdapter.BluetoothLeScanner != null) { Trace.Message($"Adapter >=21: Starting a scan for devices. ScanMode: {ScanMode}"); if (hasFilter) { if (scanFilterOptions.HasServiceIds) { Trace.Message($"Service UUID Scan Filters: {string.Join(", ", scanFilterOptions.ServiceUuids)}"); } if (scanFilterOptions.HasServiceData) { Trace.Message($"Service Data Scan Filters: {string.Join(", ", scanFilterOptions.ServiceDataFilters.ToString())}"); } if (scanFilterOptions.HasManufacturerIds) { Trace.Message($"Manufacturer Id Scan Filters: {string.Join(", ", scanFilterOptions.ManufacturerDataFilters.ToString())}"); } if (scanFilterOptions.HasDeviceAddresses) { Trace.Message($"Device Address Scan Filters: {string.Join(", ", scanFilterOptions.DeviceAddresses)}"); } if (scanFilterOptions.HasDeviceNames) { Trace.Message($"Device Name Scan Filters: {string.Join(", ", scanFilterOptions.DeviceNames)}"); } } _bluetoothAdapter.BluetoothLeScanner.StartScan(scanFilters, ssb.Build(), _api21ScanCallback); } else { Trace.Message("Adapter >= 21: Scan failed. Bluetooth is probably off"); } }
protected override Task StartScanningForDevicesNativeAsync(Guid[] serviceUuids, CancellationToken scanCancellationToken) { // clear out the list DiscoveredDevices.Clear(); if (serviceUuids == null || !serviceUuids.Any()) { if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop) { Trace.Message("Adapter < 21: Starting a scan for devices."); //without filter #pragma warning disable 618 _bluetoothAdapter.StartLeScan(_api18ScanCallback); #pragma warning restore 618 } else { Trace.Message("Adapter >= 21: Starting a scan for devices."); if (_bluetoothAdapter.BluetoothLeScanner != null) { _bluetoothAdapter.BluetoothLeScanner.StartScan(_api21ScanCallback); } else { Trace.Message("Adapter >= 21: Scan failed. Bluetooth is probably off"); } } } else { if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop) { var uuids = serviceUuids.Select(u => UUID.FromString(u.ToString())).ToArray(); Trace.Message("Adapter < 21: Starting a scan for devices."); #pragma warning disable 618 _bluetoothAdapter.StartLeScan(uuids, _api18ScanCallback); #pragma warning restore 618 } else { Trace.Message("Adapter >=21: Starting a scan for devices with service Id {0}.", serviceUuids.First()); var scanFilters = new List <ScanFilter>(); foreach (var serviceUuid in serviceUuids) { var sfb = new ScanFilter.Builder(); sfb.SetServiceUuid(ParcelUuid.FromString(serviceUuid.ToString())); scanFilters.Add(sfb.Build()); } var ssb = new ScanSettings.Builder(); //ssb.SetCallbackType(ScanCallbackType.AllMatches); if (_bluetoothAdapter.BluetoothLeScanner != null) { _bluetoothAdapter.BluetoothLeScanner.StartScan(scanFilters, ssb.Build(), _api21ScanCallback); } else { Trace.Message("Adapter >= 21: Scan failed. Bluetooth is probably off"); } } } return(Task.FromResult(true)); }