Beispiel #1
0
        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));
        }
Beispiel #2
0
        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;
        }
Beispiel #3
0
        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}");
            }
        }
Beispiel #4
0
        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");
            }
        }
Beispiel #5
0
 //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);
     }
 }
Beispiel #6
0
        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());
        }
Beispiel #7
0
        // First step
        public void Disconnect()
        {
            if (_gatt != null)
            {
                ClearServices();

                _gatt.Disconnect();
            }
            else
            {
                Trace.Message("[Warning]: Can't disconnect {0}. Gatt is null.", Name);
            }
        }
Beispiel #8
0
        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
        }
Beispiel #9
0
        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);
            }
        }
Beispiel #10
0
        /// <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);
            }
        }
        /// <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);
            }
        }
Beispiel #12
0
        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");
            }
        }
        /// <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;
            }
        }
Beispiel #14
0
        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
                       ));
        }
Beispiel #15
0
 public override void OnScanFailed(ScanFailure errorCode)
 {
     Trace.Message("Adapter: Scan failed with code {0}", errorCode);
     base.OnScanFailed(errorCode);
 }
Beispiel #16
0
            public void OnLeScan(BluetoothDevice bleDevice, int rssi, byte[] scanRecord)
            {
                Trace.Message("Adapter.LeScanCallback: " + bleDevice.Name);

                _adapter.HandleDiscoveredDevice(new Device(_adapter, bleDevice, null, rssi, scanRecord));
            }
Beispiel #17
0
        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));
        }
Beispiel #18
0
        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");
            }
        }