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

        public IPv6ToBleIPv6AddressCharacteristic(
            GattLocalCharacteristic characteristic,
            GenericGattService service,
            IPAddress address
            ) : base(characteristic, service)
        {
            Ipv6Address = address;
            Value       = GattHelpers.ConvertByteArrayToBuffer(Ipv6Address.GetAddressBytes());
        }
Beispiel #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;
            }
        }
Beispiel #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);
        }