//---------------------------------------------------------------------
        // Constructor
        //---------------------------------------------------------------------

        public IPv6ToBleIPv6AddressCharacteristic(
            GattLocalCharacteristic characteristic,
            GenericGattService service,
            IPAddress address
            ) : base(characteristic, service)
        {
            Ipv6Address = address;
            Value       = GattHelpers.ConvertByteArrayToBuffer(Ipv6Address.GetAddressBytes());
        }
예제 #2
0
        //---------------------------------------------------------------------
        // Event handlers for advertisements
        //---------------------------------------------------------------------

        //
        // Specifies behavior for when an advertisement is received by the
        // watcher. Because the IPv6 Over Bluetooth Low Energy project is based
        // on 6LoWPAN principles, routers actively watch for possible
        // recipients when they have a packet to deliver. Receiving an
        // advertisement means that there is a suitable node (or other router)
        // within range that can receive the packet, so we transmit it here.
        //
        private async void OnAdvertisementReceived(
            BluetoothLEAdvertisementWatcher watcher,
            BluetoothLEAdvertisementReceivedEventArgs eventArgs
            )
        {
            BluetoothError status = BluetoothError.Success;

            // Variables for the remote device, the IPv6ToBle packet processing
            // service, IPSS, and the device's characteristics
            BluetoothLEDevice device = null;
            GattDeviceService internetProtocolSupportService         = null;
            GattDeviceService ipv6ToBlePacketProcessingService       = null;
            IReadOnlyList <GattCharacteristic> deviceCharacteristics = null;

            // Variables to hold the characteristics with which we need to
            // interact
            GattCharacteristic ipv6PacketWriteCharacteristic = null;
            GattCharacteristic ipv6AddressCharacteristic     = null;
            IPAddress          deviceAddress = null;

            //
            // Step 1
            // Verify we have recevied a proper advertisement from the server
            // by checking its manufacturer data. This is kind of redundant
            // because we filter advertisements based on this, but it's not a
            // bad idea.
            //
            IList <BluetoothLEManufacturerData> manufacturerDataList = eventArgs.Advertisement.ManufacturerData;

            if (manufacturerDataList.Count == 0)
            {
                status = BluetoothError.OtherError;
                Debug.WriteLine("No manufacturer data in the advertisement.");
                goto Exit;
            }
            else
            {
                // There should only be one if it's one of our advertisements
                BluetoothLEManufacturerData manufacturerData = manufacturerDataList[0];

                // Verify it's the IPv6ToBle manufacturer name
                string manufacturerDataCompanyId = string.Format("0x{0}",
                                                                 manufacturerData.CompanyId.ToString("X")
                                                                 );
                if (manufacturerDataCompanyId != "0xDEDE")
                {
                    status = BluetoothError.OtherError;
                    Debug.WriteLine("Manufacturer Company ID did not match " +
                                    "IPv6ToBle."
                                    );
                    goto Exit;
                }
            }

            //
            // Step 2
            // Connect to the device
            //
            try
            {
                // Connect based on the device's Bluetooth address
                device = await BluetoothLEDevice.FromBluetoothAddressAsync(eventArgs.BluetoothAddress);

                if (device == null)
                {
                    status = BluetoothError.DeviceNotConnected;
                    Debug.WriteLine("Error connecting to device.");
                    goto Exit;
                }
            } catch (Exception e) when(e.HResult == Constants.E_DEVICE_NOT_AVAILABLE)
            {
                status = BluetoothError.RadioNotAvailable;
                Debug.WriteLine("Bluetooth radio is not on.");
                goto Exit;
            }

            //
            // Step 3
            // Enumerate the GATT services to get the
            // IPv6ToBlePacketProcessingService and Internet Protocol Support
            // Service (IPSS)
            //
            if (device != null)
            {
                // Retrieve the list of services from the device (uncached)
                GattDeviceServicesResult servicesResult = await device.GetGattServicesAsync(BluetoothCacheMode.Uncached);

                if (servicesResult.Status == GattCommunicationStatus.Success)
                {
                    var services = servicesResult.Services;
                    Debug.WriteLine($"Found {services.Count} services");

                    // Iterate through the list of services and check if
                    // both services we require are there
                    foreach (GattDeviceService service in services)
                    {
                        Guid uuid = service.Uuid;

                        // Check for IPv6ToBle Packet Write Service
                        if (uuid == Constants.IPv6ToBlePacketProcessingServiceUuid)
                        {
                            ipv6ToBlePacketProcessingService = service;
                            continue;
                        }

                        // Check for IPSS
                        ushort shortId = GattHelpers.ConvertUuidToShortId(uuid);
                        if (shortId == (ushort)GattHelpers.SigAssignedGattNativeUuid.InternetProtocolSupport)
                        {
                            internetProtocolSupportService = service;
                            continue;
                        }
                    }
                }
            }

            // Report error if the device was not running our packet processing
            // service for some reason
            if (ipv6ToBlePacketProcessingService == null ||
                internetProtocolSupportService == null)
            {
                status = BluetoothError.OtherError;
                Debug.WriteLine("Device did not have the " +
                                "IPv6ToBlePacketProcessingService running or" +
                                " available, or did not have the Internet" +
                                " Protocol Support Service running or " +
                                "available."
                                );
                goto Exit;
            }

            //
            // Step 4
            // Enumerate the GATT characteristics
            //
            try
            {
                // Verify we can access the service
                DeviceAccessStatus accessStatus = await ipv6ToBlePacketProcessingService.RequestAccessAsync();

                if (accessStatus == DeviceAccessStatus.Allowed)
                {
                    // Enumerate the characteristics
                    GattCharacteristicsResult characteristicsResult =
                        await ipv6ToBlePacketProcessingService.GetCharacteristicsAsync(BluetoothCacheMode.Uncached);

                    if (characteristicsResult.Status == GattCommunicationStatus.Success)
                    {
                        deviceCharacteristics = characteristicsResult.Characteristics;
                    }
                    else
                    {
                        status = BluetoothError.OtherError;
                        Debug.WriteLine("Could not access the packet " +
                                        "processing service."
                                        );
                        goto Exit;
                    }
                }
                else
                {
                    // Not granted access
                    status = BluetoothError.NotSupported;

                    Debug.WriteLine("Could not access the packet " +
                                    "processing service."
                                    );
                    goto Exit;
                }
            }
            catch (Exception e)
            {
                status = BluetoothError.DeviceNotConnected;
                Debug.WriteLine("Could not read characteristics due to " +
                                "permissions issues. " + e.Message
                                );
                goto Exit;
            }

            if (deviceCharacteristics != null)
            {
                // Find the IPv6 Address and packet write characteristics
                foreach (GattCharacteristic characteristic in deviceCharacteristics)
                {
                    if (characteristic.Uuid == Constants.IPv6ToBleIPv6AddressCharacteristicUuid)
                    {
                        ipv6AddressCharacteristic = characteristic;
                    }

                    if (characteristic.Uuid == Constants.IPv6ToBlePacketWriteCharacteristicUuid)
                    {
                        ipv6PacketWriteCharacteristic = characteristic;
                    }
                }
            }

            if (ipv6PacketWriteCharacteristic == null ||
                ipv6AddressCharacteristic == null)
            {
                status = BluetoothError.OtherError;
                Debug.WriteLine("Could not access the IPv6 address" +
                                " characteristic and the packet write" +
                                " characteristic.");
                goto Exit;
            }

            // Get the device's IPv6 address from the characteristic
            if (ipv6AddressCharacteristic != null)
            {
                GattReadResult readResult = await ipv6AddressCharacteristic.ReadValueAsync();

                if (readResult.Status == GattCommunicationStatus.Success)
                {
                    deviceAddress = new IPAddress(GattHelpers.ConvertBufferToByteArray(readResult.Value));
                }
                else
                {
                    status = BluetoothError.OtherError;
                    Debug.WriteLine("Could not read the device's IPv6 address" +
                                    " from the remote characteristic."
                                    );
                    goto Exit;
                }
            }

            //
            // Step 5
            // Write the packet now that we have verified that the device is
            // supported and is either the destination or in the path to the
            // destination
            //
            GattCommunicationStatus writeStatus =
                await ipv6PacketWriteCharacteristic.WriteValueAsync(GattHelpers.ConvertByteArrayToBuffer(Packet));

            if (writeStatus != GattCommunicationStatus.Success)
            {
                status = BluetoothError.OtherError;
                Debug.WriteLine("Could not write the IPv6 packet to the" +
                                " remote device");
            }

Exit:

            if (status != BluetoothError.Success)
            {
                TransmittedSuccessfully = false;
            }
            else
            {
                TransmittedSuccessfully = true;
            }
        }
예제 #3
0
        /// <summary>
        /// Writes a packet to a given device.
        /// </summary>
        /// <param name="targetDevice">The target device.</param>
        /// <param name="packet">The packet, compressed or not.</param>
        /// <param name="compressedHeaderLength">Optional. The length of the compressed header if the caller is sending a compressed packet.</param>
        /// <param name="payloadLength">Optional. The paylaod length of the packet. Only needed if the caller is sending a compressed packet.</param>
        /// <returns></returns>
        public static async Task <bool> WritePacketAsync(
            DeviceInformation targetDevice,
            byte[] packet,
            int compressedHeaderLength,
            int payloadLength
            )
        {
            BluetoothError status = BluetoothError.Success;

            // Variables for the remote device, the IPv6ToBle packet processing
            // service, IPSS, and the device's characteristics
            BluetoothLEDevice device = null;
            GattDeviceService ipv6ToBlePacketProcessingService       = null;
            IReadOnlyList <GattCharacteristic> deviceCharacteristics = null;

            // Variables for the remote packet write characteristic
            GattCharacteristic ipv6PacketWriteCharacteristic        = null;
            GattCharacteristic compressedHeaderLengthCharacteristic = null;
            GattCharacteristic payloadLengthCharacteristic          = null;

            // Variables for timing GATT communication latency and transmission time
            Stopwatch gattLatencyTimer      = new Stopwatch();
            Stopwatch gattTransmissionTimer = new Stopwatch();

            //
            // Step 1
            // Connect to the device
            //

            // Start timing how long it takes to perform pre-transmission GATT
            // communications
            gattLatencyTimer.Start();

            try
            {
                // Connect based on the device's Bluetooth address
                device = await BluetoothLEDevice.FromIdAsync(targetDevice.Id);

                if (device == null)
                {
                    status = BluetoothError.DeviceNotConnected;
                    Debug.WriteLine("Error connecting to device.");
                    goto Exit;
                }
            }
            catch (Exception e) when(e.HResult == Constants.E_DEVICE_NOT_AVAILABLE)
            {
                status = BluetoothError.RadioNotAvailable;
                Debug.WriteLine("Bluetooth radio is not on.");
                goto Exit;
            }

            //
            // Step 2
            // Enumerate the GATT services to get the
            // IPv6ToBlePacketProcessingService
            //
            if (device != null)
            {
                // Retrieve the list of services from the device (uncached)
                GattDeviceServicesResult servicesResult = await device.GetGattServicesAsync(BluetoothCacheMode.Cached);

                if (servicesResult.Status == GattCommunicationStatus.Success)
                {
                    var services = servicesResult.Services;
                    Debug.WriteLine($"Found {services.Count} services when " +
                                    "querying services for packet writing."
                                    );

                    // Iterate through the list of services and check if
                    // both services we require are there
                    foreach (GattDeviceService service in services)
                    {
                        Guid uuid = service.Uuid;

                        // Check for IPv6ToBle Packet Write Service
                        if (uuid == Constants.IPv6ToBlePacketProcessingServiceUuid)
                        {
                            ipv6ToBlePacketProcessingService = service;
                            break;
                        }
                    }
                }
            }

            // Report error if the device was not running our packet processing
            // service for some reason
            if (ipv6ToBlePacketProcessingService == null)
            {
                status = BluetoothError.OtherError;
                Debug.WriteLine("Device did not have the " +
                                "IPv6ToBlePacketProcessingService running or" +
                                " available."
                                );
                goto Exit;
            }

            //
            // Step 3
            // Enumerate the GATT characteristics
            //
            try
            {
                // Verify we can access the service
                DeviceAccessStatus accessStatus = await ipv6ToBlePacketProcessingService.RequestAccessAsync();

                if (accessStatus == DeviceAccessStatus.Allowed)
                {
                    // Enumerate the characteristics
                    GattCharacteristicsResult characteristicsResult =
                        await ipv6ToBlePacketProcessingService.GetCharacteristicsAsync(BluetoothCacheMode.Cached);

                    if (characteristicsResult.Status == GattCommunicationStatus.Success)
                    {
                        deviceCharacteristics = characteristicsResult.Characteristics;
                    }
                    else
                    {
                        status = BluetoothError.OtherError;
                        Debug.WriteLine("Could not access the packet " +
                                        "processing service."
                                        );
                        goto Exit;
                    }
                }
                else
                {
                    // Not granted access
                    status = BluetoothError.NotSupported;

                    Debug.WriteLine("Could not access the packet " +
                                    "processing service."
                                    );
                    goto Exit;
                }
            }
            catch (Exception e)
            {
                status = BluetoothError.DeviceNotConnected;
                Debug.WriteLine("Could not read characteristics due to " +
                                " permissions issues. " + e.Message
                                );
                goto Exit;
            }

            // Find the required characteristics for packet writing
            if (deviceCharacteristics != null)
            {
                foreach (GattCharacteristic characteristic in deviceCharacteristics)
                {
                    if (characteristic.Uuid == Constants.IPv6ToBlePacketWriteCharacteristicUuid)
                    {
                        ipv6PacketWriteCharacteristic = characteristic;
                    }
                    if (characteristic.Uuid == Constants.IPv6ToBleCompressedHeaderLengthCharacteristicUuid)
                    {
                        compressedHeaderLengthCharacteristic = characteristic;
                    }
                    if (characteristic.Uuid == Constants.IPv6ToBlePayloadLengthCharacteristicUuid)
                    {
                        payloadLengthCharacteristic = characteristic;
                    }
                }
            }

            if (ipv6PacketWriteCharacteristic == null ||
                compressedHeaderLengthCharacteristic == null ||
                payloadLengthCharacteristic == null)
            {
                status = BluetoothError.OtherError;
                Debug.WriteLine("Could not access all three characteristics " +
                                "required for packet writing."
                                );
                goto Exit;
            }

            // Stop the GATT latency timer now that we are ready to do the actual
            // transmission
            gattLatencyTimer.Stop();
            Debug.WriteLine("GATT communications completed successfully. Total " +
                            $"time: {gattLatencyTimer.ElapsedMilliseconds} milliseconds."
                            );

            //
            // Step 5
            // Write the packet now that we have verified that the device is
            // supported and is either the destination or in the path to the
            // destination
            //

            // Start the GATT transmission timer
            gattTransmissionTimer.Start();

            //DataWriter writer = new DataWriter();

            //// Write the compressed header length
            //writer.WriteInt32(compressedHeaderLength);
            //GattCommunicationStatus writeStatus = await compressedHeaderLengthCharacteristic.WriteValueAsync(writer.DetachBuffer());
            //if (writeStatus != GattCommunicationStatus.Success)
            //{
            //    status = BluetoothError.OtherError;
            //    Debug.WriteLine("Could not write compressed header length.");
            //    goto Exit;
            //}

            //// Write the payload length
            //writer = new DataWriter();
            //writer.WriteInt32(payloadLength);
            //writeStatus = await payloadLengthCharacteristic.WriteValueAsync(writer.DetachBuffer());
            //if (writeStatus != GattCommunicationStatus.Success)
            //{
            //    status = BluetoothError.OtherError;
            //    Debug.WriteLine("Could not write payload length.");
            //    goto Exit;
            //}

            // Write the packet itself last so the receiver knows that it has
            // all needed data when this characteristic is written to
            GattCommunicationStatus writeStatus = await ipv6PacketWriteCharacteristic.WriteValueAsync(GattHelpers.ConvertByteArrayToBuffer(packet),
                                                                                                      GattWriteOption.WriteWithoutResponse
                                                                                                      );

            if (writeStatus != GattCommunicationStatus.Success)
            {
                status = BluetoothError.OtherError;
                Debug.WriteLine("Could not write the IPv6 packet to the" +
                                " remote device");
                goto Exit;
            }

            // Stop the GATT transmission timer and record the time if successful
            gattTransmissionTimer.Stop();
            long microseconds = gattTransmissionTimer.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));

            Debug.WriteLine("GATT transmission completed successfully. Total " +
                            $"time: {microseconds} microseconds."
                            );

Exit:

            // Dispose of the service and device that we accessed, then force
            // a garbage collection to destroy the objects and fully disconnect
            // from the remote GATT server and device. This is as a workaround
            // for a current Windows bug that doesn't properly disconnect
            // devices, as well as a workaround for the Broadcomm Bluetooth LE
            // driver on the Raspberry Pi 3 that can't handle multiple connects
            // and reconnects if it thinks it's still occupied.
            //
            // Additionally, at this step, if you had connected any events
            // to the services or characteristics, you'd have to do that first.
            // But we didn't do that here, so no need.

            ipv6ToBlePacketProcessingService?.Dispose();
            device?.Dispose();
            device = null;
            GC.Collect();

            if (status != BluetoothError.Success)
            {
                // Stop the timers if we failed
                if (gattLatencyTimer.IsRunning)
                {
                    gattLatencyTimer.Stop();
                }
                if (gattTransmissionTimer.IsRunning)
                {
                    gattTransmissionTimer.Stop();
                }

                return(false);
            }

            return(true);
        }
        /// <summary>
        /// Awaits the reception of a packet on the packet write characteristic
        /// of the packet write service of the local GATT server.
        ///
        /// This is so a server can know when a packet has been received and
        /// deal with it accordingly.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        private async void WatchForPacketReception(
            object sender,
            PropertyChangedEventArgs eventArgs
            )
        {
            if (sender == localPacketWriteCharacteristic)
            {
                if (eventArgs.PropertyName == "Value")
                {
                    // Get the received packet
                    Packet = GattHelpers.ConvertBufferToByteArray(localPacketWriteCharacteristic.Value);

                    // Get the other two characteristics' info for decompressing
                    // the packet
                    DataReader reader = DataReader.FromBuffer(localCompressedHeaderLengthCharacteristic.Value);
                    CompressedHeaderLength = reader.ReadInt32();
                    reader        = DataReader.FromBuffer(localPayloadLengthCharacteristic.Value);
                    PayloadLength = reader.ReadInt32();

                    Debug.WriteLine("Received this packet over " +
                                    "Bluetooth: " + Utilities.BytesToString(packet));

                    // TESTING: Start the timer for header decompression to time
                    // total transmission/reception time
                    bleReceptionTimer.Start();

                    // Decompress the packet
                    try
                    {
                        headerCompression.UncompressHeaderIphc(packet,
                                                               compressedHeaderLength,
                                                               payloadLength
                                                               );
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine("Exception occurred during header " +
                                        "decompression. Message: " + e.Message
                                        );
                        bleReceptionTimer.Stop();
                        return;
                    }

                    bleReceptionTimer.Stop();
                    Debug.WriteLine($"Header decompression took {bleReceptionTimer.ElapsedMilliseconds} milliseconds.");

                    // Only send it back out if this device is not the destination;
                    // in other words, if this device is a middle router in the
                    // subnet
                    IPAddress destinationAddress = GetDestinationAddressFromPacket(
                        packet
                        );

                    // Check if the packet is NOT for this device
                    bool packetIsForThisDevice = false;

                    packetIsForThisDevice = IPAddress.Equals(destinationAddress, generatedLocalIPv6AddressForNode);

                    if (!packetIsForThisDevice)
                    {
                        // Check if the message is in the local message cache or not
                        if (messageCache.Contains(packet))
                        {
                            Debug.WriteLine("This packet is not for this device and" +
                                            " has been seen before."
                                            );
                            return;
                        }

                        // If this message has not been seen before, add it to the
                        // message queue and remove the oldest if there would now
                        // be more than 10
                        if (messageCache.Count < 10)
                        {
                            messageCache.Enqueue(packet);
                        }
                        else
                        {
                            messageCache.Dequeue();
                            messageCache.Enqueue(packet);
                        }

                        await SendPacketOverBluetoothLE(packet,
                                                        destinationAddress
                                                        );
                    }
                    else
                    {
                        // It's for this device. Check if it has been seen before
                        // or not.

                        // Check if the message is in the local message cache or not
                        if (messageCache.Contains(packet))
                        {
                            Debug.WriteLine("This packet is for this device, but " +
                                            "has been seen before."
                                            );
                            return;
                        }

                        // If this message has not been seen before, add it to the
                        // message queue and remove the oldest if there would now
                        // be more than 10
                        if (messageCache.Count < 10)
                        {
                            messageCache.Enqueue(packet);
                        }
                        else
                        {
                            messageCache.Dequeue();
                            messageCache.Enqueue(packet);
                        }

                        // Send the packet to the driver for inbound injection
                        SendPacketToDriverForInboundInjection(packet);
                    }
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Awaits the reception of a packet on the packet write characteristic
        /// of the packet write service of the local GATT server.
        ///
        /// This is so a server can know when a packet has been received and
        /// deal with it accordingly.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        private async void WatchForPacketReception(
            object sender,
            PropertyChangedEventArgs eventArgs
            )
        {
            if (sender == localPacketWriteCharacteristic)
            {
                if (eventArgs.PropertyName == "Value")
                {
                    // Set the packet byte array to the received value for others to
                    // read or retrieve
                    Packet = GattHelpers.ConvertBufferToByteArray(localPacketWriteCharacteristic.Value);

                    Debug.WriteLine("Received this packet over " +
                                    "Bluetooth: " + Utilities.BytesToString(packet));

                    // Only send it back out if this device is not the destination;
                    // in other words, if this device is a middle router in the
                    // subnet
                    IPAddress destinationAddress = GetDestinationAddressFromPacket(
                        packet
                        );

                    // Check if the packet is NOT for this device
                    bool packetIsForThisDevice = false;

                    packetIsForThisDevice = IPAddress.Equals(destinationAddress, generatedLocalIPv6AddressForNode);

                    if (!packetIsForThisDevice)
                    {
                        // Check if the message is in the local message cache or not
                        if (messageCache.Contains(packet))
                        {
                            Debug.WriteLine("This packet is not for this device and" +
                                            " has been seen before."
                                            );
                            return;
                        }

                        // If this message has not been seen before, add it to the
                        // message queue and remove the oldest if there would now
                        // be more than 10
                        if (messageCache.Count < 10)
                        {
                            messageCache.Enqueue(packet);
                        }
                        else
                        {
                            messageCache.Dequeue();
                            messageCache.Enqueue(packet);
                        }

                        await SendPacketOverBluetoothLE(packet,
                                                        destinationAddress
                                                        );
                    }
                    else
                    {
                        // It's for this device. Check if it has been seen before
                        // or not.

                        // Check if the message is in the local message cache or not
                        if (messageCache.Contains(packet))
                        {
                            Debug.WriteLine("This packet is for this device, but " +
                                            "has been seen before."
                                            );
                            return;
                        }

                        // If this message has not been seen before, add it to the
                        // message queue and remove the oldest if there would now
                        // be more than 10
                        if (messageCache.Count < 10)
                        {
                            messageCache.Enqueue(packet);
                        }
                        else
                        {
                            messageCache.Dequeue();
                            messageCache.Enqueue(packet);
                        }

                        // Send the packet to the driver for inbound injection
                        SendPacketToDriverForInboundInjection(packet);
                    }
                }
            }
        }
        //---------------------------------------------------------------------
        // Methods for device connection after discovery. Enumerate services
        // on each discovered device and add supported devices to a dictionary
        // that maps devices to their link-local IPv6 addresses.
        //---------------------------------------------------------------------

        // Connects to each found device and enumerates available GATT
        // services, then only adds devices to the list that support both the
        // IPSSS and our IPv6ToBle packet writing service. This method is
        // called after the initial device discovery phase.
        public async Task PopulateSupportedDevices()
        {
            //
            // Step 1
            // Check for empty list in case we couldn't find anything
            //
            if (foundDevices.Count == 0)
            {
                return;
            }

            //
            // Step 2
            // Connect to each previously found device and enumerate its
            // services. If it supports both IPSS and IPv6ToBle Packet Writing
            // Service, add it to the list.
            //
            foreach (DeviceInformation deviceInfo in foundDevices)
            {
                BluetoothLEDevice  currentDevice = null;
                GattDeviceService  ipv6ToBlePacketProcessingService = null;
                GattCharacteristic ipv6AddressCharacteristic        = null;
                IPAddress          ipv6Address = null;

                bool hasInternetProtocolSupportService = false;
                bool hasIPv6ToBlePacketWriteService    = false;

                try
                {
                    // Connect. This is recommended to do on a UI thread
                    // normally because it may prompt for consent, but for our
                    // purposes in this application it will auto-accept and we
                    // don't have to use a UI thread.
                    currentDevice = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id);

                    if (currentDevice == null)
                    {
                        Debug.WriteLine($"Failed to connect to device {deviceInfo.Id}");
                    }
                }
                catch (Exception e) when(e.HResult == Constants.E_DEVICE_NOT_AVAILABLE)
                {
                    Debug.WriteLine("Bluetooth radio is not on.");
                }

                // Enumerate the GATT services with GetGattServicesAsync
                if (currentDevice != null)
                {
                    // Retrieve the list of services from the device (uncached)
                    GattDeviceServicesResult servicesResult = await currentDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached);

                    if (servicesResult.Status == GattCommunicationStatus.Success)
                    {
                        var services = servicesResult.Services;
                        Debug.WriteLine($"Found {services.Count} services for" +
                                        $" device {deviceInfo.Id}"
                                        );

                        // Iterate through the list of services and check if
                        // both services we require are there
                        foreach (GattDeviceService service in services)
                        {
                            Guid uuid = service.Uuid;

                            // Check for IPSS
                            ushort shortId = GattHelpers.ConvertUuidToShortId(uuid);
                            if (shortId == (ushort)GattHelpers.SigAssignedGattNativeUuid.InternetProtocolSupport)
                            {
                                hasInternetProtocolSupportService = true;
                                continue;
                            }

                            // Check for IPv6ToBle Packet Write Service
                            if (uuid == Constants.IPv6ToBlePacketProcessingServiceUuid)
                            {
                                hasIPv6ToBlePacketWriteService   = true;
                                ipv6ToBlePacketProcessingService = service;
                                continue;
                            }
                        }
                    }
                }

                // Query the device's IP address - enumerate characteristics
                // for the IPv6ToBlePacketProcessingService and read from the
                // IPv6 address characteristic to map devices to their
                // addresses
                if (hasInternetProtocolSupportService &&
                    hasIPv6ToBlePacketWriteService &&
                    ipv6ToBlePacketProcessingService != null)
                {
                    IReadOnlyList <GattCharacteristic> characteristics = null;

                    try
                    {
                        // Verify we can access the device's packet processing service
                        DeviceAccessStatus accessStatus = await ipv6ToBlePacketProcessingService.RequestAccessAsync();

                        if (accessStatus == DeviceAccessStatus.Allowed)
                        {
                            // Enumerate the characteristics
                            GattCharacteristicsResult characteristicsResult =
                                await ipv6ToBlePacketProcessingService.GetCharacteristicsAsync(BluetoothCacheMode.Uncached);

                            if (characteristicsResult.Status == GattCommunicationStatus.Success)
                            {
                                characteristics = characteristicsResult.Characteristics;
                            }
                            else
                            {
                                Debug.WriteLine("Could not access the packet" +
                                                " processing service."
                                                );
                                // On error, act as if there were no characteristics
                                characteristics = new List <GattCharacteristic>();
                            }
                        }
                        else
                        {
                            // Not granted access
                            Debug.WriteLine("Could not access the packet" +
                                            "processing service."
                                            );
                            // On error, act as if there were no characteristics
                            characteristics = new List <GattCharacteristic>();
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine("Could not read characteristics due to" +
                                        " permissions issues. " + e.Message
                                        );
                        // On error, act as if there were no characteristics
                        characteristics = new List <GattCharacteristic>();
                    }

                    // Find the IPv6 address characteristic
                    foreach (GattCharacteristic characteristic in characteristics)
                    {
                        if (characteristic.Uuid == Constants.IPv6ToBleIPv6AddressCharacteristicUuid)
                        {
                            ipv6AddressCharacteristic = characteristic;
                            break;
                        }
                    }

                    // Get the IPv6 address from the characteristic
                    if (ipv6AddressCharacteristic != null)
                    {
                        GattReadResult readResult = await ipv6AddressCharacteristic.ReadValueAsync();

                        if (readResult.Status == GattCommunicationStatus.Success)
                        {
                            ipv6Address = new IPAddress(GattHelpers.ConvertBufferToByteArray(readResult.Value));
                        }
                    }

                    // Finally, add the deviceInfo/IP address pair to the
                    // dictionary
                    if (ipv6Address != null)
                    {
                        supportedBleDevices.Add(ipv6Address, deviceInfo);
                    }
                }

                // Dispose of the service and device that we accessed, then force
                // a garbage collection to destroy the objects and fully disconnect
                // from the remote GATT server and device. This is as a workaround
                // for a current Windows bug that doesn't properly disconnect
                // devices, as well as a workaround for the Broadcomm Bluetooth LE
                // driver on the Raspberry Pi 3 that can't handle multiple connects
                // and reconnects if it thinks it's still occupied.
                //
                // Additionally, at this step, if you had connected any events
                // to the services or characteristics, you'd have to disconnect
                // them first. But we didn't do that here, so no need.

                ipv6ToBlePacketProcessingService?.Dispose();
                currentDevice?.Dispose();
                currentDevice = null;
                GC.Collect();
            }
        }
예제 #7
0
        //---------------------------------------------------------------------
        // Asynchronous initialization
        //---------------------------------------------------------------------

        public override async Task InitAsync()
        {
            await CreateServiceProvider(Constants.IPv6ToBlePacketProcessingServiceUuid);

            GattLocalCharacteristicResult characteristicResult = null;

            //
            // Step 1
            // Create the packet write characteristic
            //
            GattLocalCharacteristic createdPacketWriteCharacteristic = null;

            characteristicResult = await ServiceProvider.Service.CreateCharacteristicAsync(
                Constants.IPv6ToBlePacketWriteCharacteristicUuid,
                GattHelpers.packetWriteParameters
                );

            //
            // Step 2
            // Assign the created packet write characteristic to this service's internal one
            //
            GattHelpers.GetCharacteristicFromResult(characteristicResult,
                                                    ref createdPacketWriteCharacteristic
                                                    );
            if (createdPacketWriteCharacteristic != null)
            {
                PacketWriteCharacteristic = new IPv6ToBlePacketWriteCharacteristic(
                    createdPacketWriteCharacteristic,
                    this
                    );
            }

            characteristicResult = null;

            //
            // Step 3
            // Create the compressed header length characteristic
            //
            GattLocalCharacteristic createdCompressedHeaderLengthCharacteristic = null;

            characteristicResult = await ServiceProvider.Service.CreateCharacteristicAsync(
                Constants.IPv6ToBleCompressedHeaderLengthCharacteristicUuid,
                GattHelpers.packetWriteParameters
                );

            //
            // Step 4
            // Assign the created compressed header length characteristic to this service's internal one
            //
            GattHelpers.GetCharacteristicFromResult(characteristicResult,
                                                    ref createdCompressedHeaderLengthCharacteristic
                                                    );
            if (createdCompressedHeaderLengthCharacteristic != null)
            {
                CompressedHeaderLengthCharacteristic = new CompressedHeaderLengthCharacteristic(
                    createdCompressedHeaderLengthCharacteristic,
                    this
                    );
            }

            characteristicResult = null;

            //
            // Step 5
            // Create the payload length characteristic
            //
            GattLocalCharacteristic createdPayloadLengthCharacteristic = null;

            characteristicResult = await ServiceProvider.Service.CreateCharacteristicAsync(
                Constants.IPv6ToBlePayloadLengthCharacteristicUuid,
                GattHelpers.packetWriteParameters
                );

            //
            // Step 6
            // Assign the created payload length characteristic to this service's internal one
            //
            GattHelpers.GetCharacteristicFromResult(characteristicResult,
                                                    ref createdPayloadLengthCharacteristic
                                                    );
            if (createdPayloadLengthCharacteristic != null)
            {
                PayloadLengthCharacteristic = new PayloadLengthCharacteristic(
                    createdPayloadLengthCharacteristic,
                    this
                    );
            }

            characteristicResult = null;

            //
            // Step 7
            // Create the IPv6 address read characteristic
            //

            // Generate the device's link-local IPv6 address
            IPAddress address = await StatelessAddressConfiguration.GenerateLinkLocalAddressFromBlThRadioIdAsync(2);

            if (address == null)
            {
                Debug.WriteLine("Could not generate a link-local IPv6 address" +
                                " from the Bluetooth address."
                                );
                return;
            }

            // Create the characteristic
            GattLocalCharacteristic createdIPv6AddressReadCharacteristic = null;

            characteristicResult = await ServiceProvider.Service.CreateCharacteristicAsync(
                Constants.IPv6ToBleIPv6AddressCharacteristicUuid,
                GattHelpers.ipv6AddressReadParameters
                );

            //
            // Step 8
            // Assign the created IPv6 address read characteristic to this service's internal one
            //
            GattHelpers.GetCharacteristicFromResult(characteristicResult,
                                                    ref createdIPv6AddressReadCharacteristic
                                                    );
            if (createdIPv6AddressReadCharacteristic != null)
            {
                IPv6AddressCharacteristic = new IPv6ToBleIPv6AddressCharacteristic(
                    createdIPv6AddressReadCharacteristic,
                    this,
                    address
                    );
            }
        }