`BluetoothLEAdvertisementWatcher` is required for LE device discovery on WinRT. Several WinRT apps, like Zwift will try getting this interface, and crash if they are not able to.
-- v5: windows.devices.bluetooth: Implement BluetoothLEAdvertisementWatcher::get_{Min, Max}OutOfRangeTimeout. windows.devices.bluetooth: Implement BluetoothLEAdvertisementWatcher::get_{Min, Max}SamplingInterval. windows.devices.bluetooth: Add stubs for BluetoothLEAdvertisementWatcher. windows.devices.bluetooth/tests: Add tests for IBluetoothLEAdvertisementWatcher. include: Add windows.devices.bluetooth.advertisement.idl.
From: Vibhav Pant vibhavp@gmail.com
--- include/Makefile.in | 1 + ...indows.devices.bluetooth.advertisement.idl | 522 ++++++++++++++++++ include/windows.devices.bluetooth.idl | 42 ++ 3 files changed, 565 insertions(+) create mode 100644 include/windows.devices.bluetooth.advertisement.idl
diff --git a/include/Makefile.in b/include/Makefile.in index 662b149da32..57dd4c3dc26 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -866,6 +866,7 @@ SOURCES = \ windows.applicationmodel.idl \ windows.data.json.idl \ windows.data.xml.dom.idl \ + windows.devices.bluetooth.advertisement.idl \ windows.devices.bluetooth.genericattributeprofile.idl \ windows.devices.bluetooth.idl \ windows.devices.bluetooth.rfcomm.idl \ diff --git a/include/windows.devices.bluetooth.advertisement.idl b/include/windows.devices.bluetooth.advertisement.idl new file mode 100644 index 00000000000..f976db1627f --- /dev/null +++ b/include/windows.devices.bluetooth.advertisement.idl @@ -0,0 +1,522 @@ +/* + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifdef __WIDL__ +#pragma winrt ns_prefix +#endif + +#ifndef DO_NO_IMPORTS +import "inspectable.idl"; +import "asyncinfo.idl"; +import "eventtoken.idl"; +import "windowscontracts.idl"; +import "windows.foundation.idl"; +import "windows.storage.streams.idl"; +import "windows.devices.bluetooth.idl"; +#endif + +namespace Windows.Devices.Bluetooth { + typedef enum BluetoothAddressType BluetoothAddressType; + typedef enum BluetoothError BluetoothError; + + runtimeclass BluetoothSignalStrengthFilter; +} + +namespace Windows.Devices.Bluetooth.Advertisement { + typedef enum BluetoothLEAdvertisementFlags BluetoothLEAdvertisementFlags; + typedef enum BluetoothLEAdvertisementWatcherStatus BluetoothLEAdvertisementWatcherStatus; + typedef enum BluetoothLEScanningMode BluetoothLEScanningMode; + typedef enum BluetoothLEAdvertisementType BluetoothLEAdvertisementType; + typedef enum BluetoothLEAdvertisementPhyType BluetoothLEAdvertisementPhyType; + + interface IBluetoothLEManufacturerData; + interface IBluetoothLEAdvertisementDataSection; + interface IBluetoothLEAdvertisementDataSectionFactory; + interface IBluetoothLEManufacturerData; + interface IBluetoothLEManufacturerDataFactory; + interface IBluetoothLEAdvertisementWatcher; + interface IBluetoothLEAdvertisementWatcher2; + interface IBluetoothLEAdvertisementWatcher3; + interface IBluetoothLEAdvertisementFilter; + interface IBluetoothLEAdvertisementReceivedEventArgs; + interface IBluetoothLEAdvertisementReceivedEventArgs2; + interface IBluetoothLEAdvertisementReceivedEventArgs3; + interface IBluetoothLEAdvertisementWatcherStoppedEventArgs; + interface IBluetoothLEAdvertisementBytePattern; + interface IBluetoothLEAdvertisementBytePatternFactory; + interface IBluetoothLEAdvertisementScanParameters; + interface IBluetoothLEAdvertisementScanParametersStatics; + + + runtimeclass BluetoothLEAdvertisement; + runtimeclass BluetoothLEManufacturerData; + runtimeclass BluetoothLEAdvertisementDataSection; + runtimeclass BluetoothLEManufacturerData; + runtimeclass BluetoothLEAdvertisementWatcher; + runtimeclass BluetoothLEAdvertisementFilter; + runtimeclass BluetoothLEAdvertisementReceivedEventArgs; + runtimeclass BluetoothLEAdvertisementWatcherStoppedEventArgs; + runtimeclass BluetoothLEAdvertisementBytePattern; + runtimeclass BluetoothLEAdvertisementScanParameters; + + declare { + interface Windows.Foundation.IReference<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementFlags>; + interface Windows.Foundation.Collections.IVector<GUID>; + interface Windows.Foundation.Collections.IVector<Windows.Devices.Bluetooth.Advertisement.BluetoothLEManufacturerData *>; + interface Windows.Foundation.Collections.IVector<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementDataSection *>; + interface Windows.Foundation.Collections.IVector<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementBytePattern *>; + interface Windows.Foundation.Collections.IVectorView<GUID>; + interface Windows.Foundation.Collections.IVectorView<Windows.Devices.Bluetooth.Advertisement.BluetoothLEManufacturerData *>; + interface Windows.Foundation.Collections.IVectorView<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementDataSection *>; + interface Windows.Foundation.Collections.IVectorView<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementBytePattern *>; + interface Windows.Foundation.Collections.IIterable<GUID>; + interface Windows.Foundation.Collections.IIterable<Windows.Devices.Bluetooth.Advertisement.BluetoothLEManufacturerData *>; + interface Windows.Foundation.Collections.IIterable<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementDataSection *>; + interface Windows.Foundation.Collections.IIterable<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementBytePattern *>; + interface Windows.Foundation.Collections.IIterator<GUID>; + interface Windows.Foundation.Collections.IIterator<Windows.Devices.Bluetooth.Advertisement.BluetoothLEManufacturerData *>; + interface Windows.Foundation.Collections.IIterator<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementDataSection *>; + interface Windows.Foundation.Collections.IIterator<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementBytePattern *>; + interface Windows.Foundation.TypedEventHandler<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher *, Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementReceivedEventArgs *>; + interface Windows.Foundation.TypedEventHandler<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher *, Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcherStoppedEventArgs *>; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + flags + ] + enum BluetoothLEAdvertisementFlags + { + None = 0x00, + LimitedDiscoverableMode = 0x01, + GeneralDiscoverableMode = 0x02, + ClassicNotSupported = 0x04, + DualModeControllerCapable = 0x08, + DualModeHostCapable = 0x10, + }; + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + ] + enum BluetoothLEAdvertisementPublisherStatus + { + Created = 0, + Waiting = 1, + Started = 2, + Stopping = 3, + Stopped = 4, + Aborted = 5, + }; + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + ] + enum BluetoothLEAdvertisementType + { + ConnectableUndirected = 0, + ConnectableDirected = 1, + ScannableUndirected = 2, + NonConnectableUndirected = 3, + ScanResponse = 4, + [contract(Windows.Foundation.UniversalApiContract, 10.0)] + Extended = 5, + }; + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + ] + enum BluetoothLEAdvertisementWatcherStatus + { + Created = 0, + Started = 1, + Stopping = 2, + Stopped = 3, + Aborted = 4, + }; + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + ] + enum BluetoothLEScanningMode + { + Passive = 0, + Active = 1, + [contract(Windows.Foundation.UniversalApiContract, 10.0)] + None = 2, + }; + + [ + contract(Windows.Foundation.UniversalApiContract, 19.0), + ] + enum BluetoothLEAdvertisementPhyType + { + Unspecified = 0, + Uncoded1MPhy = 1, + Uncoded2MPhy = 2, + CodedPhy = 3, + }; + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisement), + uuid(066fb2b7-33d1-4e7d-8367-cf81d0f79653) + ] + interface IBluetoothLEAdvertisement : IInspectable + { + [propget] HRESULT Flags([out, retval] Windows.Foundation.IReference<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementFlags> **value); + [propput] HRESULT Flags([in] Windows.Foundation.IReference<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementFlags> *value); + [propget] HRESULT LocalName([out, retval] HSTRING *value); + [propput] HRESULT LocalName([in] HSTRING value); + [propget] HRESULT ServiceUuids([out, retval] Windows.Foundation.Collections.IVector<GUID> **value); + [propget] HRESULT ManufacturerData([out, retval] Windows.Foundation.Collections.IVector<Windows.Devices.Bluetooth.Advertisement.BluetoothLEManufacturerData *> **value); + [propget] HRESULT DataSections([out, retval] Windows.Foundation.Collections.IVector<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementDataSection *> **value); + HRESULT GetManufacturerDataByCompanyId([in] UINT16 id, [out, retval] Windows.Foundation.Collections.IVectorView<Windows.Devices.Bluetooth.Advertisement.BluetoothLEManufacturerData *> **value); + HRESULT GetSectionsByType([in] BYTE type, [out, retval] Windows.Foundation.Collections.IVectorView<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementDataSection *> **value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + activatable(Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass BluetoothLEAdvertisement + { + [default] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisement; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementDataSection), + uuid(d7213314-3a43-40f9-b6f0-92bfefc34ae3) + ] + interface IBluetoothLEAdvertisementDataSection : IInspectable + { + [propget] HRESULT DataType([out, retval] BYTE *value); + [propput] HRESULT DataType([in] BYTE value); + [propget] HRESULT Data([out, retval] Windows.Storage.Streams.IBuffer **value); + [propput] HRESULT Data([in] Windows.Storage.Streams.IBuffer *value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementDataSection), + uuid(e7a40942-a845-4045-bf7e-3e9971db8a6b) + ] + interface IBluetoothLEAdvertisementDataSectionFactory : IInspectable + { + HRESULT Create([in] BYTE type, [in] Windows.Storage.Streams.IBuffer *data, + [out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementDataSection **value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + activatable(Windows.Foundation.UniversalApiContract, 1.0), + activatable(Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementDataSectionFactory, Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass BluetoothLEAdvertisementDataSection + { + [default] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementDataSection; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEManufacturerData), + uuid(912dba18-6963-4533-b061-4694dafb34e5) + ] + interface IBluetoothLEManufacturerData : IInspectable + { + [propget] HRESULT CompanyId([out, retval] UINT16 *value); + [propput] HRESULT CompanyId([in] UINT16 value); + [propget] HRESULT Data([out, retval] Windows.Storage.Streams.IBuffer **value); + [propput] HRESULT Data([in] Windows.Storage.Streams.IBuffer *value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEManufacturerData), + uuid(c09b39f8-319a-441e-8de5-66a81e877a6c) + ] + interface IBluetoothLEManufacturerDataFactory : IInspectable + { + HRESULT Create([in] UINT16 id, [in] Windows.Storage.Streams.IBuffer *data, + [out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEManufacturerData **value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + activatable(Windows.Foundation.UniversalApiContract, 1.0), + activatable(Windows.Devices.Bluetooth.Advertisement.IBluetoothLEManufacturerDataFactory, Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass BluetoothLEManufacturerData + { + [default] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEManufacturerData; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher), + uuid(a6ac336f-f3d3-4297-8d6c-c81ea6623f40) + ] + interface IBluetoothLEAdvertisementWatcher : IInspectable + { + [propget] HRESULT MinSamplingInterval([out, retval] Windows.Foundation.TimeSpan *value); + [propget] HRESULT MaxSamplingInterval([out, retval] Windows.Foundation.TimeSpan *value); + [propget] HRESULT MinOutOfRangeTimeout([out, retval] Windows.Foundation.TimeSpan *value); + [propget] HRESULT MaxOutOfRangeTimeout([out, retval] Windows.Foundation.TimeSpan *value); + [propget] HRESULT Status([out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcherStatus *value); + [propget] HRESULT ScanningMode([out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEScanningMode *value); + [propput] HRESULT ScanningMode([in] Windows.Devices.Bluetooth.Advertisement.BluetoothLEScanningMode value); + [propget] HRESULT SignalStrengthFilter([out, retval] Windows.Devices.Bluetooth.BluetoothSignalStrengthFilter **value); + [propput] HRESULT SignalStrengthFilter([in] Windows.Devices.Bluetooth.BluetoothSignalStrengthFilter *value); + [propget] HRESULT AdvertisementFilter([out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementFilter **value); + [propput] HRESULT AdvertisementFilter([in] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementFilter *value); + HRESULT Start(); + HRESULT Stop(); + [eventadd] HRESULT Received([in] Windows.Foundation.TypedEventHandler<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher *, Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementReceivedEventArgs *> *handler, + [out, retval] EventRegistrationToken *token); + [eventremove] HRESULT Received([in] EventRegistrationToken token); + [eventadd] HRESULT Stopped([in] Windows.Foundation.TypedEventHandler<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher *, Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcherStoppedEventArgs *> *handler, + [out, retval] EventRegistrationToken *token); + [eventremove] HRESULT Stopped([in] EventRegistrationToken token); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 10.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher), + uuid(01bf26bc-b164-5805-90a3-e8a7997ff225) + ] + interface IBluetoothLEAdvertisementWatcher2 : IInspectable + { + [propget] HRESULT AllowExtendedAdvertisements([out, retval] boolean *value); + [propput] HRESULT AllowExtendedAdvertisements([in] boolean value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 19.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher), + uuid(14d980be-4002-5dbe-8519-ffca6ca389f0) + ] + interface IBluetoothLEAdvertisementWatcher3 : IInspectable + { + [propget] HRESULT UseUncoded1MPhy([out, retval] boolean *value); + [propput] HRESULT UseUncoded1MPhy([in] boolean value); + [propget] HRESULT UseCodedPhy([out, retval] boolean *value); + [propput] HRESULT UseCodedPhy([in] boolean value); + [propget] HRESULT ScanParameters([out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementScanParameters **value); + [propput] HRESULT ScanParameters([in] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementScanParameters *value); + [propget] HRESULT UseHardwareFilter([out, retval] boolean *value); + [propput] HRESULT UseHardwareFilter([in] boolean value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher), + uuid(9aaf2d56-39ac-453e-b32a-85c657e017f1) + ] + interface IBluetoothLEAdvertisementWatcherFactory : IInspectable + { + HRESULT Create([in] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementFilter *filter, + [out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher **value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + activatable(Windows.Foundation.UniversalApiContract, 1.0), + activatable(Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementWatcherFactory, Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass BluetoothLEAdvertisementWatcher + { + [default] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementWatcher; + [contract(Windows.Foundation.UniversalApiContract, 10.0)] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementWatcher2; + [contract(Windows.Foundation.UniversalApiContract, 19.0)] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementWatcher3; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementFilter), + uuid(131eb0d3-d04e-47b1-837e-49405bf6f80f) + ] + interface IBluetoothLEAdvertisementFilter : IInspectable + { + [propget] HRESULT Advertisement([out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisement **value); + [propput] HRESULT Advertisement([in] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisement *value); + [propget] HRESULT BytePatterns([out, retval] Windows.Foundation.Collections.IVector<Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementBytePattern *> **value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + activatable(Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass BluetoothLEAdvertisementFilter + { + [default] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementFilter; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 10.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementReceivedEventArgs), + uuid(27987ddf-e596-41be-8d43-9e6731d4a913) + ] + interface IBluetoothLEAdvertisementReceivedEventArgs : IInspectable + { + [propget] HRESULT RawSignalStrengthInDBm([out, retval] INT16 *value); + [propget] HRESULT BluetoothAddress([out, retval] UINT64 *value); + [propget] HRESULT AdvertisementType([out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementType *value); + [propget] HRESULT Timestamp([out, retval] Windows.Foundation.DateTime *value); + [propget] HRESULT Advertisement([out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisement **value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 10.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementReceivedEventArgs), + uuid(12d9c87b-0399-5f0e-a348-53b02b6b162e) + ] + interface IBluetoothLEAdvertisementReceivedEventArgs2 : IInspectable + { + [propget] HRESULT BluetoothAddressType([out, retval] Windows.Devices.Bluetooth.BluetoothAddressType *value); + [propget] HRESULT TransmitPowerLevelInDBm([out, retval] Windows.Foundation.IReference<INT16> **value); + [propget] HRESULT IsAnonymous([out, retval] boolean *value); + [propget] HRESULT IsConnectable([out, retval] boolean *value); + [propget] HRESULT IsScannable([out, retval] boolean *value); + [propget] HRESULT IsDirected([out, retval] boolean *value); + [propget] HRESULT IsScanResponse([out, retval] boolean *value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 10.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementReceivedEventArgs), + uuid(8d204b54-ff86-5d84-a25a-137dccd96f7a) + ] + interface IBluetoothLEAdvertisementReceivedEventArgs3 : IInspectable + { + [propget] HRESULT PrimaryPhy([out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementPhyType *value); + [propget] HRESULT SecondaryPhy([out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementPhyType *value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass BluetoothLEAdvertisementReceivedEventArgs + { + [default] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementReceivedEventArgs; + [contract(Windows.Foundation.UniversalApiContract, 10.0)] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementReceivedEventArgs2; + [contract(Windows.Foundation.UniversalApiContract, 19.0)] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementReceivedEventArgs3; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcherStoppedEventArgs), + uuid(dd40f84d-e7b9-43e3-9c04-0685d085fd8c) + ] + interface IBluetoothLEAdvertisementWatcherStoppedEventArgs : IInspectable + { + [propget] HRESULT Error([out, retval] Windows.Devices.Bluetooth.BluetoothError *value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass BluetoothLEAdvertisementWatcherStoppedEventArgs + { + [default] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementWatcherStoppedEventArgs; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementBytePattern), + uuid(fbfad7f2-b9c5-4a08-bc51-502f8ef68a79) + ] + interface IBluetoothLEAdvertisementBytePattern : IInspectable + { + [propget] HRESULT DataType([out, retval] BYTE *value); + [propput] HRESULT DataType([in] BYTE value); + [propget] HRESULT Offset([out, retval] INT16 *value); + [propput] HRESULT Offset([in] INT16 value); + [propget] HRESULT Data([out, retval] Windows.Storage.Streams.IBuffer **value); + [propput] HRESULT Data([in] Windows.Storage.Streams.IBuffer *value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementBytePattern), + uuid(c2e24d73-fd5c-4ec3-be2a-9ca6fa11b7bd) + ] + interface IBluetoothLEAdvertisementBytePatternFactory : IInspectable + { + HRESULT Create([in] BYTE type, [in] INT16 offset, [in] Windows.Storage.Streams.IBuffer *data, + [out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementBytePattern **value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + activatable(Windows.Foundation.UniversalApiContract, 1.0), + activatable(Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementBytePatternFactory, Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass BluetoothLEAdvertisementBytePattern + { + [default] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementBytePattern; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 19.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementScanParameters), + uuid(94f91413-63d9-53bd-af4c-e6b1a6514595) + ] + interface IBluetoothLEAdvertisementScanParameters : IInspectable + { + [propget] HRESULT ScanWindow([out, retval] UINT16 *value); + [propget] HRESULT ScanInterval([out, retval] UINT16 *value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 19.0), + exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementScanParameters), + uuid(548e39cd-3c9e-5f8d-b5e1-adebed5c357c) + ] + interface IBluetoothLEAdvertisementScanParametersStatics : IInspectable + { + HRESULT CoexistenceOptimized([out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementScanParameters **value); + HRESULT LowLatency([out, retval] Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementScanParameters **value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 19.0), + static(Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementScanParametersStatics, Windows.Foundation.UniversalApiContract, 19.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass BluetoothLEAdvertisementScanParameters + { + [default] interface Windows.Devices.Bluetooth.Advertisement.IBluetoothLEAdvertisementScanParameters; + } +} diff --git a/include/windows.devices.bluetooth.idl b/include/windows.devices.bluetooth.idl index ef14b25ba8c..9e27db46ca2 100644 --- a/include/windows.devices.bluetooth.idl +++ b/include/windows.devices.bluetooth.idl @@ -41,6 +41,7 @@ namespace Windows.Devices.Bluetooth { typedef enum BluetoothMinorClass BluetoothMinorClass; typedef enum BluetoothServiceCapabilities BluetoothServiceCapabilities; typedef enum BluetoothError BluetoothError; + typedef enum BluetoothAddressType BluetoothAddressType;
interface IBluetoothAdapter; interface IBluetoothAdapter2; @@ -65,12 +66,14 @@ namespace Windows.Devices.Bluetooth { interface IBluetoothLEDevice6; interface IBluetoothLEDeviceStatics; interface IBluetoothLEDeviceStatics2; + interface IBluetoothSignalStrengthFilter;
runtimeclass BluetoothAdapter; runtimeclass BluetoothClassOfDevice; runtimeclass BluetoothDevice; runtimeclass BluetoothDeviceId; runtimeclass BluetoothLEDevice; + runtimeclass BluetoothSignalStrengthFilter;
namespace Rfcomm { runtimeclass RfcommDeviceService; @@ -245,6 +248,17 @@ namespace Windows.Devices.Bluetooth { TransportNotSupported = 9, };
+ [ + contract(Windows.Foundation.UniversalApiContract, 2.0) + ] + enum BluetoothAddressType + { + Public = 0, + Random = 1, + [contract(Windows.Foundation.UniversalApiContract, 4.0)] + Unspecified = 2, + }; + [ contract(Windows.Foundation.UniversalApiContract, 4.0), exclusiveto(Windows.Devices.Bluetooth.BluetoothAdapter), @@ -473,4 +487,32 @@ namespace Windows.Devices.Bluetooth { [contract(Windows.Foundation.UniversalApiContract, 13.0)] interface Windows.Devices.Bluetooth.IBluetoothLEDevice6; interface Windows.Foundation.IClosable; } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Devices.Bluetooth.BluetoothSignalStrengthFilter), + uuid(df7b7391-6bb5-4cfe-90b1-5d7324edcf7f) + ] + interface IBluetoothSignalStrengthFilter : IInspectable + { + [propget] HRESULT InRangeThresholdInDBm([out, retval] Windows.Foundation.IReference<INT16> **value); + [propput] HRESULT InRangeThresholdInDBm([in] Windows.Foundation.IReference<INT16> *value); + [propget] HRESULT OutOfRangeThresholdInDBm([out, retval] Windows.Foundation.IReference<INT16> **value); + [propput] HRESULT OutOfRangeThresholdInDBm([in] Windows.Foundation.IReference<INT16> *value); + [propget] HRESULT OutOfRangeTimeout([out, retval] Windows.Foundation.IReference<Windows.Foundation.TimeSpan> **value); + [propput] HRESULT OutOfRangeTimeout([in] Windows.Foundation.IReference<Windows.Foundation.TimeSpan> *value); + [propget] HRESULT SamplingInterval([out, retval] Windows.Foundation.IReference<Windows.Foundation.TimeSpan> **value); + [propput] HRESULT SamplingInterval([in] Windows.Foundation.IReference<Windows.Foundation.TimeSpan> *value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + activatable(Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass BluetoothSignalStrengthFilter + { + [default] interface Windows.Devices.Bluetooth.IBluetoothSignalStrengthFilter; + } }
From: Vibhav Pant vibhavp@gmail.com
--- .../tests/bluetooth.c | 460 ++++++++++++++++++ 1 file changed, 460 insertions(+)
diff --git a/dlls/windows.devices.bluetooth/tests/bluetooth.c b/dlls/windows.devices.bluetooth/tests/bluetooth.c index 5b78e1e176d..37f76596659 100644 --- a/dlls/windows.devices.bluetooth/tests/bluetooth.c +++ b/dlls/windows.devices.bluetooth/tests/bluetooth.c @@ -16,6 +16,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "winerror.h" #define COBJMACROS #include "initguid.h" #include <stdarg.h> @@ -32,7 +33,9 @@ #define WIDL_using_Windows_Networking #include "windows.networking.connectivity.h" #include "windows.networking.h" +#define WIDL_using_Windows_Devices_Bluetooth_Advertisement #define WIDL_using_Windows_Devices_Bluetooth +#include "windows.devices.bluetooth.advertisement.h" #include "windows.devices.bluetooth.rfcomm.h" #include "windows.devices.bluetooth.h"
@@ -129,6 +132,83 @@ static WINAPI IAsyncOperationCompletedHandler_IInspectable *inspectable_async_ha return &impl->iface; }
+struct inspectable_event_handler +{ + ITypedEventHandler_IInspectable_IInspectable iface; + const GUID *iid; + void (*callback)( IInspectable *, IInspectable *, void * ); + void *data; + LONG ref; +}; + +static inline struct inspectable_event_handler *impl_from_ITypedEventHandler_IInspectable_IInspectable( ITypedEventHandler_IInspectable_IInspectable *iface ) +{ + return CONTAINING_RECORD( iface, struct inspectable_event_handler, iface ); +} + +static HRESULT WINAPI inspectable_event_handler_QueryInterface( ITypedEventHandler_IInspectable_IInspectable *iface, REFIID iid, void **out ) +{ + struct inspectable_event_handler *impl = impl_from_ITypedEventHandler_IInspectable_IInspectable( iface ); + + if (winetest_debug > 1) trace( "(%p, %s, %p)\n", iface, debugstr_guid( iid ), out ); + if (IsEqualGUID( iid, &IID_IUnknown ) || IsEqualGUID( iid, &IID_IAgileObject) || IsEqualGUID( iid, impl->iid )) + { + ITypedEventHandler_IInspectable_IInspectable_AddRef((*out = &impl->iface.lpVtbl)); + return S_OK; + } + + *out = NULL; + if (winetest_debug > 1) trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + return E_NOINTERFACE; +} + +static ULONG WINAPI inspectable_event_handler_AddRef( ITypedEventHandler_IInspectable_IInspectable *iface ) +{ + struct inspectable_event_handler *impl = impl_from_ITypedEventHandler_IInspectable_IInspectable( iface ); + return InterlockedIncrement( &impl->ref ); +} + +static ULONG WINAPI inspectable_event_handler_Release( ITypedEventHandler_IInspectable_IInspectable *iface ) +{ + struct inspectable_event_handler *impl = impl_from_ITypedEventHandler_IInspectable_IInspectable( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + if (!ref) free( impl ); + return ref; +} + +static HRESULT WINAPI inspectable_event_handler_Invoke( ITypedEventHandler_IInspectable_IInspectable *iface, IInspectable *arg1, IInspectable *arg2 ) +{ + struct inspectable_event_handler *impl = impl_from_ITypedEventHandler_IInspectable_IInspectable( iface ); + + if (winetest_debug > 1) trace( "(%p, %p, %p)\n", iface, arg1, arg2 ); + impl->callback( arg1, arg2, impl->data ); + return S_OK; +} + +static const ITypedEventHandler_IInspectable_IInspectableVtbl inspectable_event_handler_vtbl = { + /* IUnknown */ + inspectable_event_handler_QueryInterface, + inspectable_event_handler_AddRef, + inspectable_event_handler_Release, + /* ITypedEventHandler<IInspectable *, IInspectable *> */ + inspectable_event_handler_Invoke +}; + +static ITypedEventHandler_IInspectable_IInspectable *inspectable_event_handler_create( REFIID iid, void (*callback)( IInspectable *, IInspectable *, void * ), + void *data ) +{ + struct inspectable_event_handler *handler; + + if (!(handler = calloc( 1, sizeof( *handler )))) return NULL; + handler->iface.lpVtbl = &inspectable_event_handler_vtbl; + handler->iid = iid; + handler->callback = callback; + handler->data = data; + handler->ref = 1; + return &handler->iface; +} + static void await_bluetoothledevice( int line, IAsyncOperation_BluetoothLEDevice *async ) { IAsyncOperationCompletedHandler_IInspectable *handler; @@ -337,6 +417,385 @@ static void test_BluetoothLEDeviceStatics( void ) IBluetoothLEDeviceStatics_Release( statics ); }
+/* See https://www.bluetooth.com/specifications/assigned-numbers, 2.3, Common Data Types */ +#define BLE_AD_TYPE_FLAGS 0x01 +#define BLE_AD_TYPE_SHORT_LOCAL_NAME 0x08 +#define BLE_AD_TYPE_FULL_LOCAL_NAME 0x09 +#define BLE_AD_TYPE_MFG_SPECIFIC 0xFF + +#define test_advertisement_has_data_type( a, t ) test_advertisement_has_data_type_( __LINE__, (a), (t) ) +static BOOL test_advertisement_has_data_type_( int line, IBluetoothLEAdvertisement *adv, BYTE type ) +{ + IVectorView_BluetoothLEAdvertisementDataSection *section = NULL; + HRESULT hr; + + hr = IBluetoothLEAdvertisement_GetSectionsByType( adv, type, §ion ); + todo_wine ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok_(__FILE__, line)( !!section, "got section %p.\n", section ); + if (SUCCEEDED( hr )) + { + UINT32 len = 0; + + hr = IVectorView_BluetoothLEAdvertisementDataSection_get_Size( section, &len ); + IVectorView_BluetoothLEAdvertisementDataSection_Release( section ); + ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); + return !!len; + } + + return FALSE; +} + +static const char *debugstr_bluetooth_addr( INT64 addr ) +{ + const BYTE *bytes = (BYTE *)&addr; + return wine_dbg_sprintf( "%02X:%02X:%02X:%02X:%02X:%02X", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0] ); +} + +struct adv_watcher_received_data +{ + LONG *stopped; + DWORD received; +}; + +static void test_watcher_advertisement_received( IInspectable *arg1, IInspectable *arg2, void *user_data ) +{ + IVector_BluetoothLEAdvertisementDataSection *data_sections = NULL; + IReference_BluetoothLEAdvertisementFlags *flags_ref = NULL; + struct adv_watcher_received_data *data = user_data; + IBluetoothLEAdvertisementReceivedEventArgs *event; + IVector_BluetoothLEManufacturerData *mfgs = NULL; + BluetoothLEAdvertisementType adv_type = 6; + IBluetoothLEAdvertisement *adv = NULL; + IVector_GUID *uuids = NULL; + FILETIME timestamp = {0}; + UINT32 len, mfgs_len = 0; + SYSTEMTIME st = {0}; + HSTRING name = NULL; + INT16 signal = 0; + UINT64 addr = 0; + WCHAR buf[256]; + BOOL success; + HRESULT hr; + + ok( !ReadNoFence( data->stopped ), "handler called after watcher was stopped.\n" ); + + data->received += 1; + winetest_push_context( "advertisement %lu", data->received ); + check_interface( arg1, &IID_IBluetoothLEAdvertisementWatcher ); + + hr = IInspectable_QueryInterface( arg2, &IID_IBluetoothLEAdvertisementReceivedEventArgs, (void **)&event ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( !!event, "got event %p.\n", event ); + + hr = IBluetoothLEAdvertisementReceivedEventArgs_get_RawSignalStrengthInDBm( event, &signal ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + trace( "raw signal strength: %d dBm.\n", signal ); + + hr = IBluetoothLEAdvertisementReceivedEventArgs_get_BluetoothAddress( event, &addr ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( addr, "got addr %012I64x\n", addr ); + trace( "address: %I64X (%s)\n", addr, debugstr_bluetooth_addr( addr ) ); + + hr = IBluetoothLEAdvertisementReceivedEventArgs_get_AdvertisementType( event, &adv_type ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( adv_type <= BluetoothLEAdvertisementType_Extended, "got adv_type %d\n", adv_type ); + trace( "advertisement type: %d\n", adv_type ); + + hr = IBluetoothLEAdvertisementReceivedEventArgs_get_Timestamp( event, (DateTime *)×tamp ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + success = FileTimeToSystemTime( ×tamp, &st ); + ok( success, "FileTimeToSystemTime failed: %#lx.\n", GetLastError() ); + success = SystemTimeToTzSpecificLocalTime( NULL, &st, &st ); + ok( success, "SystemTimeToTzSpecificLocalTime failed: %#lx.\n", GetLastError() ); + buf[0] = 0; + GetTimeFormatEx( NULL, TIME_FORCE24HOURFORMAT, &st, NULL, buf, ARRAY_SIZE( buf ) ); + trace("timestamp: %s\n", debugstr_w( buf )); + + hr = IBluetoothLEAdvertisementReceivedEventArgs_get_Advertisement( event, &adv ); + IBluetoothLEAdvertisementReceivedEventArgs_Release( event ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + if (FAILED( hr )) + { + skip( "get_Advertisement failed.\n" ); + winetest_pop_context(); + return; + } + + flags_ref = NULL; + hr = IBluetoothLEAdvertisement_get_Flags( adv, &flags_ref ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + if (flags_ref) + { + BluetoothLEAdvertisementFlags flags = 0; + + hr = IReference_BluetoothLEAdvertisementFlags_get_Value( flags_ref, &flags ); + IReference_BluetoothLEAdvertisementFlags_Release( flags_ref ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + trace( "flags: %#x\n", flags ); + if (flags) + todo_wine ok( test_advertisement_has_data_type( adv, BLE_AD_TYPE_FLAGS ), "missing data section for type %#x\n", BLE_AD_TYPE_FLAGS ); + } + + hr = IBluetoothLEAdvertisement_get_LocalName( adv, &name ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + trace( "name: %s\n", debugstr_hstring( name ) ); + if (!WindowsIsStringEmpty( name )) + todo_wine ok( test_advertisement_has_data_type( adv, BLE_AD_TYPE_SHORT_LOCAL_NAME ) || + test_advertisement_has_data_type( adv, BLE_AD_TYPE_FULL_LOCAL_NAME ), + "missing data sections for type %#x and %#x\n", BLE_AD_TYPE_SHORT_LOCAL_NAME, BLE_AD_TYPE_FULL_LOCAL_NAME ); + + hr = IBluetoothLEAdvertisement_get_ServiceUuids( adv, &uuids ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( !!uuids, "got uuids %p.\n", uuids ); + if (SUCCEEDED( hr )) + { + UINT32 copied = 0, i; + GUID *buf; + + len = 0; + hr = IVector_GUID_get_Size( uuids, &len ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + buf = calloc( len, sizeof( *buf ) ); + hr = IVector_GUID_GetMany( uuids, 0, len, buf, &copied ); + IVector_GUID_Release( uuids ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( copied == len, "got copied %u\n", copied ); + + for (i = 0; i < copied; i++) + trace( "uuids[%u]: %s\n", i, debugstr_guid( &buf[i] ) ); + free( buf ); + } + + hr = IBluetoothLEAdvertisement_get_ManufacturerData( adv, &mfgs ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( !!mfgs, "got mfgs %p.\n", mfgs ); + if (SUCCEEDED( hr )) + { + hr = IVector_BluetoothLEManufacturerData_get_Size( mfgs, &mfgs_len ); + IVector_BluetoothLEManufacturerData_Release( mfgs ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + trace( "manufacturer data sections: %u\n", mfgs_len ); + if (mfgs_len) + todo_wine ok( test_advertisement_has_data_type( adv, BLE_AD_TYPE_MFG_SPECIFIC ), "missing data section for type %#x\n", BLE_AD_TYPE_MFG_SPECIFIC ); + } + + hr = IBluetoothLEAdvertisement_get_DataSections( adv, &data_sections ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( !!data_sections, "got data_sections %p.\n", data_sections ); + if (SUCCEEDED( hr )) + { + UINT32 exp_len = mfgs_len; + + if (!WindowsIsStringEmpty( name )) + exp_len++; + len = 0; + /* All the above properties are derived from the raw data sections, so we can determine how many data sections we can at least expect. */ + hr = IVector_BluetoothLEAdvertisementDataSection_get_Size( data_sections, &len ); + IVector_BluetoothLEAdvertisementDataSection_Release( data_sections ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( len >= exp_len, "got len %u, should be >= %u\n", len, exp_len ); + } + winetest_pop_context(); + WindowsDeleteString( name ); + IBluetoothLEAdvertisement_Release( adv ); +} + +struct adv_watcher_stopped_data +{ + HANDLE event; + LONG called; + BOOLEAN no_radio; +}; + +static void test_watcher_stopped( IInspectable *arg1, IInspectable *arg2, void *param ) +{ + IBluetoothLEAdvertisementWatcherStoppedEventArgs *event; + BluetoothError error = BluetoothError_OtherError; + struct adv_watcher_stopped_data *data = param; + HRESULT hr; + + check_interface( arg1, &IID_IBluetoothLEAdvertisementWatcher ); + + hr = IInspectable_QueryInterface( arg2, &IID_IBluetoothLEAdvertisementWatcherStoppedEventArgs, (void **)&event ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + hr = IBluetoothLEAdvertisementWatcherStoppedEventArgs_get_Error( event, &error ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( error == BluetoothError_Success || error == BluetoothError_RadioNotAvailable, "got error %d.\n", error ); + + if (error == BluetoothError_RadioNotAvailable) + data->no_radio = TRUE; + + ok( InterlockedIncrement( &data->called ) == 1, "handler called more than once.\n" ); + SetEvent( data->event ); +} + +static void test_BluetoothLEAdvertisementWatcher( void ) +{ + const WCHAR *class_name = RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementWatcher; + ITypedEventHandler_IInspectable_IInspectable *received_handler, *stopped_handler; + EventRegistrationToken received_token = {0}, stopped_token = {0}; + struct adv_watcher_received_data received_data = {0}; + struct adv_watcher_stopped_data stopped_data = {0}; + IBluetoothSignalStrengthFilter *sig_filter = NULL; + BluetoothLEAdvertisementWatcherStatus status; + IBluetoothLEAdvertisementWatcher *watcher; + BluetoothLEScanningMode mode; + IInspectable *inspectable; + TimeSpan span; + HSTRING str; + HRESULT hr; + DWORD ret; + + WindowsCreateString( class_name, wcslen( class_name ), &str ); + hr = RoActivateInstance( str, &inspectable ); + WindowsDeleteString( str ); + todo_wine ok( hr == S_OK || broken( hr == REGDB_E_CLASSNOTREG ), "got hr %#lx.\n", hr ); + if (hr == REGDB_E_CLASSNOTREG || hr == CLASS_E_CLASSNOTAVAILABLE) + { + todo_wine win_skip( "%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w( class_name ) ); + return; + } + + check_interface( inspectable, &IID_IUnknown ); + check_interface( inspectable, &IID_IInspectable ); + check_interface( inspectable, &IID_IAgileObject ); + + hr = IInspectable_QueryInterface( inspectable, &IID_IBluetoothLEAdvertisementWatcher, (void **)&watcher ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IInspectable_Release( inspectable ); + + span.Duration = 0; + hr = IBluetoothLEAdvertisementWatcher_get_MinSamplingInterval( watcher, &span ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( span.Duration == 1000000, "got Duration %I64d.\n", span.Duration ); + + span.Duration = 0; + hr = IBluetoothLEAdvertisementWatcher_get_MaxSamplingInterval( watcher, &span ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( span.Duration == 255000000, "got Duration %I64d.\n", span.Duration ); + + span.Duration = 0; + hr = IBluetoothLEAdvertisementWatcher_get_MinOutOfRangeTimeout( watcher, &span ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( span.Duration == 10000000, "got Duration %I64d.\n", span.Duration ); + + span.Duration = 0; + hr = IBluetoothLEAdvertisementWatcher_get_MaxOutOfRangeTimeout( watcher, &span ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( span.Duration == 600000000, "got Duration %I64d.\n", span.Duration ); + + status = BluetoothLEAdvertisementWatcherStatus_Aborted; + hr = IBluetoothLEAdvertisementWatcher_get_Status( watcher, &status ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( status == BluetoothLEAdvertisementWatcherStatus_Created, "got status %u.\n", status ); + + mode = BluetoothLEScanningMode_None; + hr = IBluetoothLEAdvertisementWatcher_get_ScanningMode( watcher, &mode ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( mode == BluetoothLEScanningMode_Passive, "got status %u.\n", status ); + + hr = IBluetoothLEAdvertisementWatcher_get_SignalStrengthFilter( watcher, &sig_filter ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( !!sig_filter, "got sig_filter %p.\n", sig_filter ); + if (SUCCEEDED( hr )) + { + IReference_INT16 *int16; + IReference_TimeSpan *span; + + check_interface( sig_filter, &IID_IAgileObject ); + + int16 = (IReference_INT16 *)0xdeadbeef; + hr = IBluetoothSignalStrengthFilter_get_InRangeThresholdInDBm( sig_filter, &int16 ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( !int16, "got int16 %p.\n", int16 ); + + int16 = (IReference_INT16 *)0xdeadbeef; + hr = IBluetoothSignalStrengthFilter_get_OutOfRangeThresholdInDBm( sig_filter, &int16 ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( !int16, "got int16 %p.\n", int16 ); + + span = (IReference_TimeSpan *)0xdeadbeef; + hr = IBluetoothSignalStrengthFilter_get_OutOfRangeTimeout( sig_filter, &span ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( !span, "got span %p.\n", span ); + + span = (IReference_TimeSpan *)0xdeadbeef; + hr = IBluetoothSignalStrengthFilter_get_SamplingInterval( sig_filter, &span ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + todo_wine ok( !span, "got span %p.\n", span ); + + IBluetoothSignalStrengthFilter_Release( sig_filter ); + } + + received_data.stopped = &stopped_data.called; + received_handler = inspectable_event_handler_create( &IID_ITypedEventHandler_BluetoothLEAdvertisementWatcher_BluetoothLEAdvertisementReceivedEventArgs, + test_watcher_advertisement_received, &received_data ); + ok( !!received_handler, "inspectable_event_handler_create failed.\n" ); + hr = IBluetoothLEAdvertisementWatcher_add_Received( watcher, + (ITypedEventHandler_BluetoothLEAdvertisementWatcher_BluetoothLEAdvertisementReceivedEventArgs *)received_handler, + &received_token ); + ITypedEventHandler_IInspectable_IInspectable_Release( received_handler ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + + stopped_data.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( !!stopped_data.event, "CreateEventW failed: %lu\n", GetLastError() ); + stopped_handler = inspectable_event_handler_create( &IID_ITypedEventHandler_BluetoothLEAdvertisementWatcher_BluetoothLEAdvertisementWatcherStoppedEventArgs, + test_watcher_stopped, &stopped_data ); + ok( !!stopped_handler, "inspectable_event_handler_create failed.\n" ); + hr = IBluetoothLEAdvertisementWatcher_add_Stopped( watcher, + (ITypedEventHandler_BluetoothLEAdvertisementWatcher_BluetoothLEAdvertisementWatcherStoppedEventArgs *)stopped_handler, + &stopped_token ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + ITypedEventHandler_IInspectable_IInspectable_Release( stopped_handler ); + + hr = IBluetoothLEAdvertisementWatcher_Start( watcher ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + + /* The watcher stops with the error BluetoothError_RadioNotAvailable if no BLE radios are available. */ + ret = WaitForSingleObject( stopped_data.event, 1000 ); + if (!ret) + { + skip( "No Bluetooth radios, skipping.\n" ); + ok( stopped_data.no_radio, "got no_radio %d.\n", stopped_data.no_radio ); + CloseHandle( stopped_data.event ); + IBluetoothLEAdvertisementWatcher_Release( watcher ); + return; + } + + /* Calling Start multiple times seems to not result in any errors. */ + hr = IBluetoothLEAdvertisementWatcher_Start( watcher ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + + /* Usually there are plenty of BLE devices advertising themselves at any given point in most places, but waiting for an extended period of time + * allows testing the watcher with custom advertisements as well. */ + if (winetest_interactive) + SleepEx( 10000, FALSE ); + + hr = IBluetoothLEAdvertisementWatcher_Stop( watcher ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + ret = WaitForSingleObject( stopped_data.event, 5000 ); + todo_wine ok( !ret, "got ret %lu.\n", ret ); + /* The watcher should not have stopped with BluetoothError_RadioNotAvailable. */ + ok( !stopped_data.no_radio, "got no_radio %d.\n", stopped_data.no_radio ); + + /* Calling Stop for a stopped watcher also does not result in any errors, but also does not dispatch any more Stopped events. */ + hr = IBluetoothLEAdvertisementWatcher_Stop( watcher ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + ret = WaitForSingleObject( stopped_data.event, 1000 ); + ok( ret == WAIT_TIMEOUT, "got ret %lu.\n", ret ); + + hr = IBluetoothLEAdvertisementWatcher_remove_Stopped( watcher, stopped_token ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IBluetoothLEAdvertisementWatcher_remove_Received( watcher, received_token ); + todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); + + trace( "Received %lu advertisement packets.\n", received_data.received ); + + CloseHandle( stopped_data.event ); + IBluetoothLEAdvertisementWatcher_Release( watcher ); +} + START_TEST(bluetooth) { HRESULT hr; @@ -347,6 +806,7 @@ START_TEST(bluetooth) test_BluetoothAdapterStatics(); test_BluetoothDeviceStatics(); test_BluetoothLEDeviceStatics(); + test_BluetoothLEAdvertisementWatcher();
RoUninitialize(); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/windows.devices.bluetooth/Makefile.in | 1 + .../windows.devices.bluetooth/advertisement.c | 329 ++++++++++++++++++ dlls/windows.devices.bluetooth/classes.idl | 1 + dlls/windows.devices.bluetooth/main.c | 2 + dlls/windows.devices.bluetooth/private.h | 3 + .../tests/bluetooth.c | 4 +- 6 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 dlls/windows.devices.bluetooth/advertisement.c
diff --git a/dlls/windows.devices.bluetooth/Makefile.in b/dlls/windows.devices.bluetooth/Makefile.in index 89f3fc78abc..237a54b04fc 100644 --- a/dlls/windows.devices.bluetooth/Makefile.in +++ b/dlls/windows.devices.bluetooth/Makefile.in @@ -2,6 +2,7 @@ MODULE = windows.devices.bluetooth.dll IMPORTS = combase
SOURCES = \ + advertisement.c \ bluetoothadapter.c \ bluetoothdevice.c \ classes.idl \ diff --git a/dlls/windows.devices.bluetooth/advertisement.c b/dlls/windows.devices.bluetooth/advertisement.c new file mode 100644 index 00000000000..9e08c8873f0 --- /dev/null +++ b/dlls/windows.devices.bluetooth/advertisement.c @@ -0,0 +1,329 @@ +/* windows.Devices.Bluetooth.Advertisement Implementation + * + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL( bluetooth ); + +struct adv_watcher_factory +{ + IActivationFactory IActivationFactory_iface; + LONG ref; +}; + +static inline struct adv_watcher_factory *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct adv_watcher_factory, IActivationFactory_iface ); +} + +static HRESULT WINAPI adv_watcher_factory_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct adv_watcher_factory *impl = impl_from_IActivationFactory( iface ); + + TRACE( "(%p, %s, %p)\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + IActivationFactory_AddRef(( *out = &impl->IActivationFactory_iface )); + return S_OK; + } + + *out = NULL; + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + return E_NOINTERFACE; +} + +static ULONG WINAPI adv_watcher_factory_AddRef( IActivationFactory *iface ) +{ + struct adv_watcher_factory *impl = impl_from_IActivationFactory( iface ); + TRACE( "(%p)\n", iface ); + return InterlockedIncrement( &impl->ref ); +} + +static ULONG WINAPI adv_watcher_factory_Release( IActivationFactory *iface ) +{ + struct adv_watcher_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "(%p)\n", iface ); + return ref; +} + +static HRESULT WINAPI adv_watcher_factory_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "(%p, %p, %p): stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_factory_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "(%p, %p): stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_factory_GetTrustLevel( IActivationFactory *iface, TrustLevel *level ) +{ + FIXME( "(%p, %p): stub!\n", iface, level ); + return E_NOTIMPL; +} + +static HRESULT adv_watcher_create( IBluetoothLEAdvertisementWatcher **watcher ); + +static HRESULT WINAPI adv_watcher_factory_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + TRACE( "(%p, %p)\n", iface, instance ); + return adv_watcher_create( (IBluetoothLEAdvertisementWatcher **)instance ); +} + +static const struct IActivationFactoryVtbl adv_watcher_factory_vtbl = +{ + adv_watcher_factory_QueryInterface, + adv_watcher_factory_AddRef, + adv_watcher_factory_Release, + /* IInspectable */ + adv_watcher_factory_GetIids, + adv_watcher_factory_GetRuntimeClassName, + adv_watcher_factory_GetTrustLevel, + /* IActivationFactory */ + adv_watcher_factory_ActivateInstance +}; + +static struct adv_watcher_factory adv_watcher_factory = +{ + {&adv_watcher_factory_vtbl}, + 1 +}; + +IActivationFactory *advertisement_watcher_factory = &adv_watcher_factory.IActivationFactory_iface; + +struct adv_watcher +{ + IBluetoothLEAdvertisementWatcher IBluetoothLEAdvertisementWatcher_iface; + LONG ref; +}; + +static inline struct adv_watcher *impl_from_IBluetoothLEAdvertisementWatcher( IBluetoothLEAdvertisementWatcher *iface ) +{ + return CONTAINING_RECORD( iface, struct adv_watcher, IBluetoothLEAdvertisementWatcher_iface ); +} + +static HRESULT WINAPI adv_watcher_QueryInterface( IBluetoothLEAdvertisementWatcher *iface, REFIID iid, void **out ) +{ + struct adv_watcher *impl = impl_from_IBluetoothLEAdvertisementWatcher( iface ); + + TRACE( "(%p, %s, %p)\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IBluetoothLEAdvertisementWatcher )) + { + IBluetoothLEAdvertisementWatcher_AddRef(( *out = &impl->IBluetoothLEAdvertisementWatcher_iface )); + return S_OK; + } + + *out = NULL; + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + return E_NOINTERFACE; +} + +static ULONG WINAPI adv_watcher_AddRef( IBluetoothLEAdvertisementWatcher *iface ) +{ + struct adv_watcher *impl = impl_from_IBluetoothLEAdvertisementWatcher( iface ); + TRACE( "(%p)\n", iface ); + return InterlockedIncrement( &impl->ref ); +} + +static ULONG WINAPI adv_watcher_Release( IBluetoothLEAdvertisementWatcher *iface ) +{ + struct adv_watcher *impl = impl_from_IBluetoothLEAdvertisementWatcher( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "(%p)\n", iface ); + return ref; +} + +static HRESULT WINAPI adv_watcher_GetIids( IBluetoothLEAdvertisementWatcher *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "(%p, %p, %p): stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_GetRuntimeClassName( IBluetoothLEAdvertisementWatcher *iface, HSTRING *class_name ) +{ + FIXME( "(%p, %p): stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_GetTrustLevel( IBluetoothLEAdvertisementWatcher *iface, TrustLevel *level ) +{ + FIXME( "(%p, %p): stub!\n", iface, level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_get_MinSamplingInternal( IBluetoothLEAdvertisementWatcher *iface, TimeSpan *value ) +{ + FIXME( "(%p, %p): stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_get_MaxSamplingInternal( IBluetoothLEAdvertisementWatcher *iface, TimeSpan *value ) +{ + FIXME( "(%p, %p): stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_get_MinOutOfRangeTimeout( IBluetoothLEAdvertisementWatcher *iface, TimeSpan *value ) +{ + FIXME( "(%p, %p): stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_get_MaxOutOfRangeTimeout( IBluetoothLEAdvertisementWatcher *iface, TimeSpan *value ) +{ + FIXME( "(%p, %p): stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_get_Status( IBluetoothLEAdvertisementWatcher *iface, BluetoothLEAdvertisementWatcherStatus *status ) +{ + FIXME( "(%p, %p): stub!\n", iface, status ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_get_ScanningMode( IBluetoothLEAdvertisementWatcher *iface, BluetoothLEScanningMode *value ) +{ + FIXME( "(%p, %p): stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_put_ScanningMode( IBluetoothLEAdvertisementWatcher *iface, BluetoothLEScanningMode value ) +{ + FIXME( "(%p, %d): stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_get_SignalStrengthFilter( IBluetoothLEAdvertisementWatcher *iface, IBluetoothSignalStrengthFilter **filter ) +{ + FIXME( "(%p, %p): stub!\n", iface, filter ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_put_SignalStrengthFilter( IBluetoothLEAdvertisementWatcher *iface, IBluetoothSignalStrengthFilter *filter ) +{ + FIXME( "(%p, %p): stub!\n", iface, filter ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_get_AdvertisementFilter( IBluetoothLEAdvertisementWatcher *iface, IBluetoothLEAdvertisementFilter **filter ) +{ + FIXME( "(%p, %p): stub!\n", iface, filter ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_put_AdvertisementFilter( IBluetoothLEAdvertisementWatcher *iface, IBluetoothLEAdvertisementFilter *filter ) +{ + FIXME( "(%p, %p): stub!\n", iface, filter ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_Start( IBluetoothLEAdvertisementWatcher *iface ) +{ + FIXME( "(%p): stub!\n", iface ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_Stop( IBluetoothLEAdvertisementWatcher *iface ) +{ + FIXME( "(%p): stub!\n", iface ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_add_Received( IBluetoothLEAdvertisementWatcher *iface, + ITypedEventHandler_BluetoothLEAdvertisementWatcher_BluetoothLEAdvertisementReceivedEventArgs *handler, + EventRegistrationToken *token ) +{ + FIXME( "(%p, %p, %p): stub!\n", iface, handler, token ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_remove_Received( IBluetoothLEAdvertisementWatcher *iface, EventRegistrationToken token ) +{ + FIXME( "(%p, %I64x): stub!\n", iface, token.value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI adv_watcher_add_Stopped( IBluetoothLEAdvertisementWatcher *iface, + ITypedEventHandler_BluetoothLEAdvertisementWatcher_BluetoothLEAdvertisementWatcherStoppedEventArgs *handler, + EventRegistrationToken *token ) +{ + FIXME( "(%p, %p, %p): stub!\n", iface, handler, token ); + return E_NOTIMPL; +} + + +static HRESULT WINAPI adv_watcher_remove_Stopped( IBluetoothLEAdvertisementWatcher *iface, EventRegistrationToken token ) +{ + FIXME( "(%p, %I64x): stub!\n", iface, token.value ); + return E_NOTIMPL; +} + +static const IBluetoothLEAdvertisementWatcherVtbl adv_watcher_vtbl = +{ + /* IUnknown */ + adv_watcher_QueryInterface, + adv_watcher_AddRef, + adv_watcher_Release, + /* IInspectable */ + adv_watcher_GetIids, + adv_watcher_GetRuntimeClassName, + adv_watcher_GetTrustLevel, + /* IBluetoothLEAdvertisementWatcher */ + adv_watcher_get_MinSamplingInternal, + adv_watcher_get_MaxSamplingInternal, + adv_watcher_get_MinOutOfRangeTimeout, + adv_watcher_get_MaxOutOfRangeTimeout, + adv_watcher_get_Status, + adv_watcher_get_ScanningMode, + adv_watcher_put_ScanningMode, + adv_watcher_get_SignalStrengthFilter, + adv_watcher_put_SignalStrengthFilter, + adv_watcher_get_AdvertisementFilter, + adv_watcher_put_AdvertisementFilter, + adv_watcher_Start, + adv_watcher_Stop, + adv_watcher_add_Received, + adv_watcher_remove_Received, + adv_watcher_add_Stopped, + adv_watcher_remove_Stopped +}; + +static HRESULT adv_watcher_create( IBluetoothLEAdvertisementWatcher **watcher ) +{ + struct adv_watcher *impl; + + if (!(impl = calloc( 1, sizeof( *impl ) ))) return E_OUTOFMEMORY; + impl->IBluetoothLEAdvertisementWatcher_iface.lpVtbl = &adv_watcher_vtbl; + impl->ref = 1; + *watcher = &impl->IBluetoothLEAdvertisementWatcher_iface; + return S_OK; +} diff --git a/dlls/windows.devices.bluetooth/classes.idl b/dlls/windows.devices.bluetooth/classes.idl index ae2b58ffa2d..a2874370e52 100644 --- a/dlls/windows.devices.bluetooth/classes.idl +++ b/dlls/windows.devices.bluetooth/classes.idl @@ -36,6 +36,7 @@ import "windows.networking.sockets.idl"; import "windows.storage.streams.idl";
#define DO_NO_IMPORTS +#include "windows.devices.bluetooth.advertisement.idl" #include "windows.devices.bluetooth.genericattributeprofile.idl" #include "windows.devices.bluetooth.rfcomm.idl" #include "windows.devices.bluetooth.idl" diff --git a/dlls/windows.devices.bluetooth/main.c b/dlls/windows.devices.bluetooth/main.c index 83fd2100f06..4bfbcd17c8e 100644 --- a/dlls/windows.devices.bluetooth/main.c +++ b/dlls/windows.devices.bluetooth/main.c @@ -44,6 +44,8 @@ HRESULT WINAPI DllGetActivationFactory( HSTRING classid, IActivationFactory **fa IActivationFactory_QueryInterface( bluetoothdevice_statics_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice )) IActivationFactory_QueryInterface( bluetoothledevice_statics_factory, &IID_IActivationFactory, (void **)factory ); + if (!wcscmp( buffer, RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementWatcher)) + IActivationFactory_QueryInterface( advertisement_watcher_factory, &IID_IActivationFactory, (void **)factory );
if (*factory) return S_OK; return CLASS_E_CLASSNOTAVAILABLE; diff --git a/dlls/windows.devices.bluetooth/private.h b/dlls/windows.devices.bluetooth/private.h index c72488c2edc..66dd1d34131 100644 --- a/dlls/windows.devices.bluetooth/private.h +++ b/dlls/windows.devices.bluetooth/private.h @@ -38,10 +38,13 @@ #define WIDL_using_Windows_Devices_Bluetooth #include "windows.devices.bluetooth.rfcomm.h" #include "windows.devices.bluetooth.h" +#define WIDL_using_Windows_Devices_Bluetooth_Advertisement +#include "windows.devices.bluetooth.advertisement.h"
extern IActivationFactory *bluetoothadapter_factory; extern IActivationFactory *bluetoothdevice_statics_factory; extern IActivationFactory *bluetoothledevice_statics_factory; +extern IActivationFactory *advertisement_watcher_factory;
#define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ diff --git a/dlls/windows.devices.bluetooth/tests/bluetooth.c b/dlls/windows.devices.bluetooth/tests/bluetooth.c index 37f76596659..cebf7596a58 100644 --- a/dlls/windows.devices.bluetooth/tests/bluetooth.c +++ b/dlls/windows.devices.bluetooth/tests/bluetooth.c @@ -650,10 +650,10 @@ static void test_BluetoothLEAdvertisementWatcher( void ) WindowsCreateString( class_name, wcslen( class_name ), &str ); hr = RoActivateInstance( str, &inspectable ); WindowsDeleteString( str ); - todo_wine ok( hr == S_OK || broken( hr == REGDB_E_CLASSNOTREG ), "got hr %#lx.\n", hr ); + ok( hr == S_OK || broken( hr == REGDB_E_CLASSNOTREG ), "got hr %#lx.\n", hr ); if (hr == REGDB_E_CLASSNOTREG || hr == CLASS_E_CLASSNOTAVAILABLE) { - todo_wine win_skip( "%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w( class_name ) ); + win_skip( "%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w( class_name ) ); return; }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/windows.devices.bluetooth/advertisement.c | 10 ++++++---- dlls/windows.devices.bluetooth/tests/bluetooth.c | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/dlls/windows.devices.bluetooth/advertisement.c b/dlls/windows.devices.bluetooth/advertisement.c index 9e08c8873f0..4ab9b094e73 100644 --- a/dlls/windows.devices.bluetooth/advertisement.c +++ b/dlls/windows.devices.bluetooth/advertisement.c @@ -182,14 +182,16 @@ static HRESULT WINAPI adv_watcher_GetTrustLevel( IBluetoothLEAdvertisementWatche
static HRESULT WINAPI adv_watcher_get_MinSamplingInternal( IBluetoothLEAdvertisementWatcher *iface, TimeSpan *value ) { - FIXME( "(%p, %p): stub!\n", iface, value ); - return E_NOTIMPL; + TRACE( "(%p, %p)\n", iface, value ); + value->Duration = 1000000; + return S_OK; }
static HRESULT WINAPI adv_watcher_get_MaxSamplingInternal( IBluetoothLEAdvertisementWatcher *iface, TimeSpan *value ) { - FIXME( "(%p, %p): stub!\n", iface, value ); - return E_NOTIMPL; + TRACE( "(%p, %p)\n", iface, value ); + value->Duration = 255000000; + return S_OK; }
static HRESULT WINAPI adv_watcher_get_MinOutOfRangeTimeout( IBluetoothLEAdvertisementWatcher *iface, TimeSpan *value ) diff --git a/dlls/windows.devices.bluetooth/tests/bluetooth.c b/dlls/windows.devices.bluetooth/tests/bluetooth.c index cebf7596a58..c40ce834ab5 100644 --- a/dlls/windows.devices.bluetooth/tests/bluetooth.c +++ b/dlls/windows.devices.bluetooth/tests/bluetooth.c @@ -667,13 +667,13 @@ static void test_BluetoothLEAdvertisementWatcher( void )
span.Duration = 0; hr = IBluetoothLEAdvertisementWatcher_get_MinSamplingInterval( watcher, &span ); - todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); - todo_wine ok( span.Duration == 1000000, "got Duration %I64d.\n", span.Duration ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( span.Duration == 1000000, "got Duration %I64d.\n", span.Duration );
span.Duration = 0; hr = IBluetoothLEAdvertisementWatcher_get_MaxSamplingInterval( watcher, &span ); - todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); - todo_wine ok( span.Duration == 255000000, "got Duration %I64d.\n", span.Duration ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( span.Duration == 255000000, "got Duration %I64d.\n", span.Duration );
span.Duration = 0; hr = IBluetoothLEAdvertisementWatcher_get_MinOutOfRangeTimeout( watcher, &span );
From: Vibhav Pant vibhavp@gmail.com
--- dlls/windows.devices.bluetooth/advertisement.c | 10 ++++++---- dlls/windows.devices.bluetooth/tests/bluetooth.c | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/dlls/windows.devices.bluetooth/advertisement.c b/dlls/windows.devices.bluetooth/advertisement.c index 4ab9b094e73..ab0f82dcd45 100644 --- a/dlls/windows.devices.bluetooth/advertisement.c +++ b/dlls/windows.devices.bluetooth/advertisement.c @@ -196,14 +196,16 @@ static HRESULT WINAPI adv_watcher_get_MaxSamplingInternal( IBluetoothLEAdvertise
static HRESULT WINAPI adv_watcher_get_MinOutOfRangeTimeout( IBluetoothLEAdvertisementWatcher *iface, TimeSpan *value ) { - FIXME( "(%p, %p): stub!\n", iface, value ); - return E_NOTIMPL; + TRACE( "(%p, %p)\n", iface, value ); + value->Duration = 10000000; + return S_OK; }
static HRESULT WINAPI adv_watcher_get_MaxOutOfRangeTimeout( IBluetoothLEAdvertisementWatcher *iface, TimeSpan *value ) { - FIXME( "(%p, %p): stub!\n", iface, value ); - return E_NOTIMPL; + TRACE( "(%p, %p)\n", iface, value ); + value->Duration = 600000000; + return S_OK; }
static HRESULT WINAPI adv_watcher_get_Status( IBluetoothLEAdvertisementWatcher *iface, BluetoothLEAdvertisementWatcherStatus *status ) diff --git a/dlls/windows.devices.bluetooth/tests/bluetooth.c b/dlls/windows.devices.bluetooth/tests/bluetooth.c index c40ce834ab5..878699b4bbb 100644 --- a/dlls/windows.devices.bluetooth/tests/bluetooth.c +++ b/dlls/windows.devices.bluetooth/tests/bluetooth.c @@ -677,13 +677,13 @@ static void test_BluetoothLEAdvertisementWatcher( void )
span.Duration = 0; hr = IBluetoothLEAdvertisementWatcher_get_MinOutOfRangeTimeout( watcher, &span ); - todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); - todo_wine ok( span.Duration == 10000000, "got Duration %I64d.\n", span.Duration ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( span.Duration == 10000000, "got Duration %I64d.\n", span.Duration );
span.Duration = 0; hr = IBluetoothLEAdvertisementWatcher_get_MaxOutOfRangeTimeout( watcher, &span ); - todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr ); - todo_wine ok( span.Duration == 600000000, "got Duration %I64d.\n", span.Duration ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( span.Duration == 600000000, "got Duration %I64d.\n", span.Duration );
status = BluetoothLEAdvertisementWatcherStatus_Aborted; hr = IBluetoothLEAdvertisementWatcher_get_Status( watcher, &status );
On Fri Aug 8 15:39:28 2025 +0000, Vibhav Pant wrote:
changed this line in [version 5 of the diff](/wine/wine/-/merge_requests/8654/diffs?diff_id=199493&start_sha=133c5827ccc69fc0267ae80450459ebada92b90d#87a2e80e6c658f981ecf98325d680c221c8ef537_357_394)
Thanks
On Fri Aug 8 15:39:28 2025 +0000, Vibhav Pant wrote:
changed this line in [version 5 of the diff](/wine/wine/-/merge_requests/8654/diffs?diff_id=199493&start_sha=133c5827ccc69fc0267ae80450459ebada92b90d#87a2e80e6c658f981ecf98325d680c221c8ef537_285_304)
Thanks
On Fri Aug 8 15:39:29 2025 +0000, Vibhav Pant wrote:
changed this line in [version 5 of the diff](/wine/wine/-/merge_requests/8654/diffs?diff_id=199493&start_sha=133c5827ccc69fc0267ae80450459ebada92b90d#ba701484ca63e45554c93f972f0a30230aba9f43_454_420)
Makes sense. I have shortened the MR to only include the tests and the minor impls for BluetoothLEAdvertisementWatcher.
v5:
* Remove tests for AdvertisementFilter, DataSection and BytePattern. * Add additional definitions to IDL.
Rémi Bernon (@rbernon) commented about include/windows.devices.bluetooth.advertisement.idl:
exclusiveto(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementReceivedEventArgs),
uuid(12d9c87b-0399-5f0e-a348-53b02b6b162e)
- ]
- interface IBluetoothLEAdvertisementReceivedEventArgs2 : IInspectable
- {
[propget] HRESULT BluetoothAddressType([out, retval] Windows.Devices.Bluetooth.BluetoothAddressType *value);
[propget] HRESULT TransmitPowerLevelInDBm([out, retval] Windows.Foundation.IReference<INT16> **value);
[propget] HRESULT IsAnonymous([out, retval] boolean *value);
[propget] HRESULT IsConnectable([out, retval] boolean *value);
[propget] HRESULT IsScannable([out, retval] boolean *value);
[propget] HRESULT IsDirected([out, retval] boolean *value);
[propget] HRESULT IsScanResponse([out, retval] boolean *value);
- }
- [
contract(Windows.Foundation.UniversalApiContract, 10.0),
```suggestion:-0+0 contract(Windows.Foundation.UniversalApiContract, 19.0), ```
Rémi Bernon (@rbernon) commented about dlls/windows.devices.bluetooth/tests/bluetooth.c:
- hr = IBluetoothLEAdvertisementWatcher_get_Status( watcher, &status );
- todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr );
- todo_wine ok( status == BluetoothLEAdvertisementWatcherStatus_Created, "got status %u.\n", status );
- mode = BluetoothLEScanningMode_None;
- hr = IBluetoothLEAdvertisementWatcher_get_ScanningMode( watcher, &mode );
- todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr );
- todo_wine ok( mode == BluetoothLEScanningMode_Passive, "got status %u.\n", status );
- hr = IBluetoothLEAdvertisementWatcher_get_SignalStrengthFilter( watcher, &sig_filter );
- todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr );
- todo_wine ok( !!sig_filter, "got sig_filter %p.\n", sig_filter );
- if (SUCCEEDED( hr ))
- {
IReference_INT16 *int16;
IReference_TimeSpan *span;
I'd suggest to move these to the top, (and change the `span` variable name as it shadows the other one). When the todo_wine is going to be removed it would be nice to remove the if too.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.bluetooth/tests/bluetooth.c:
* allows testing the watcher with custom advertisements as well. */
- if (winetest_interactive)
SleepEx( 10000, FALSE );
- hr = IBluetoothLEAdvertisementWatcher_Stop( watcher );
- todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr );
- ret = WaitForSingleObject( stopped_data.event, 5000 );
- todo_wine ok( !ret, "got ret %lu.\n", ret );
- /* The watcher should not have stopped with BluetoothError_RadioNotAvailable. */
- ok( !stopped_data.no_radio, "got no_radio %d.\n", stopped_data.no_radio );
- /* Calling Stop for a stopped watcher also does not result in any errors, but also does not dispatch any more Stopped events. */
- hr = IBluetoothLEAdvertisementWatcher_Stop( watcher );
- todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr );
- ret = WaitForSingleObject( stopped_data.event, 1000 );
- ok( ret == WAIT_TIMEOUT, "got ret %lu.\n", ret );
It's usually preferred to keep the timeouts smaller if we're expecting a timeout, something like 10ms or 100ms instead should be good enough. It might not be as accurate but we want to avoid wasting time unnecessarily in the tests.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.bluetooth/tests/bluetooth.c:
- HANDLE event;
- LONG called;
- BOOLEAN no_radio;
+};
+static void test_watcher_stopped( IInspectable *arg1, IInspectable *arg2, void *param ) +{
- IBluetoothLEAdvertisementWatcherStoppedEventArgs *event;
- BluetoothError error = BluetoothError_OtherError;
- struct adv_watcher_stopped_data *data = param;
- HRESULT hr;
- check_interface( arg1, &IID_IBluetoothLEAdvertisementWatcher );
- hr = IInspectable_QueryInterface( arg2, &IID_IBluetoothLEAdvertisementWatcherStoppedEventArgs, (void **)&event );
- ok( hr == S_OK, "got hr %#lx.\n", hr );
You're leaking event here.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.bluetooth/tests/bluetooth.c:
- hr = IBluetoothLEAdvertisementReceivedEventArgs_get_Timestamp( event, (DateTime *)×tamp );
- todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr );
- success = FileTimeToSystemTime( ×tamp, &st );
- ok( success, "FileTimeToSystemTime failed: %#lx.\n", GetLastError() );
- success = SystemTimeToTzSpecificLocalTime( NULL, &st, &st );
- ok( success, "SystemTimeToTzSpecificLocalTime failed: %#lx.\n", GetLastError() );
- buf[0] = 0;
- GetTimeFormatEx( NULL, TIME_FORCE24HOURFORMAT, &st, NULL, buf, ARRAY_SIZE( buf ) );
- trace("timestamp: %s\n", debugstr_w( buf ));
- hr = IBluetoothLEAdvertisementReceivedEventArgs_get_Advertisement( event, &adv );
- IBluetoothLEAdvertisementReceivedEventArgs_Release( event );
- todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr );
- if (FAILED( hr ))
- {
skip( "get_Advertisement failed.\n" );
A message isn't really necessary if it's only going to fail under a todo_wine.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.bluetooth/tests/bluetooth.c:
- trace( "address: %I64X (%s)\n", addr, debugstr_bluetooth_addr( addr ) );
- hr = IBluetoothLEAdvertisementReceivedEventArgs_get_AdvertisementType( event, &adv_type );
- todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr );
- todo_wine ok( adv_type <= BluetoothLEAdvertisementType_Extended, "got adv_type %d\n", adv_type );
- trace( "advertisement type: %d\n", adv_type );
- hr = IBluetoothLEAdvertisementReceivedEventArgs_get_Timestamp( event, (DateTime *)×tamp );
- todo_wine ok( hr == S_OK, "got hr %#lx.\n", hr );
- success = FileTimeToSystemTime( ×tamp, &st );
- ok( success, "FileTimeToSystemTime failed: %#lx.\n", GetLastError() );
- success = SystemTimeToTzSpecificLocalTime( NULL, &st, &st );
- ok( success, "SystemTimeToTzSpecificLocalTime failed: %#lx.\n", GetLastError() );
- buf[0] = 0;
- GetTimeFormatEx( NULL, TIME_FORCE24HOURFORMAT, &st, NULL, buf, ARRAY_SIZE( buf ) );
- trace("timestamp: %s\n", debugstr_w( buf ));
If we're not checking anything I'm not sure all these traces are useful to keep in the tests. They may be interesting for development but they will also potentially increase the test output unnecessarily, which is limited in size and it will fail the tests when the limit is reached.
Rémi Bernon (@rbernon) commented about dlls/windows.devices.bluetooth/advertisement.c:
+}
+static HRESULT WINAPI adv_watcher_factory_GetTrustLevel( IActivationFactory *iface, TrustLevel *level ) +{
- FIXME( "(%p, %p): stub!\n", iface, level );
- return E_NOTIMPL;
+}
+static HRESULT adv_watcher_create( IBluetoothLEAdvertisementWatcher **watcher );
+static HRESULT WINAPI adv_watcher_factory_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{
- TRACE( "(%p, %p)\n", iface, instance );
- return adv_watcher_create( (IBluetoothLEAdvertisementWatcher **)instance );
+}
Moving the factory below, like in other WinRT classes implemented so far, removes the need to have these forward declarations.
On Mon Aug 11 08:19:02 2025 +0000, Rémi Bernon wrote:
If we're not checking anything I'm not sure all these traces are useful to keep in the tests. They may be interesting for development but they will also potentially increase the test output unnecessarily, which is limited in size and it will fail the tests when the limit is reached.
Hm. Is it okay if I place the traces behind `WINETEST_INTERACTIVE` instead?
On Tue Aug 12 13:41:30 2025 +0000, Vibhav Pant wrote:
Hm. Is it okay if I place the traces behind `WINETEST_INTERACTIVE` instead?
I think the question is whether the traces add value to the tests or if they are there just to aid development. If it's the latter I think it's usually preferred to keep these locally. In the former case and to avoid unnecessary growth of test output when no test will fail and nobody is ever going to look at them, you could also do `if (winetest_debug > 1) trace( ... )`.
On Tue Aug 12 13:48:35 2025 +0000, Rémi Bernon wrote:
I think the question is whether the traces add value to the tests or if they are there just to aid development. If it's the latter I think it's usually preferred to keep these locally. In the former case and to avoid unnecessary growth of test output when no test will fail and nobody is ever going to look at them, you could also do `if (winetest_debug > 1) trace( ... )`.
Yes, gating them behind winetest_debug is much better, thanks.
On Mon Aug 11 08:19:02 2025 +0000, Rémi Bernon wrote:
Moving the factory below, like in other WinRT classes implemented so far, removes the need to have these forward declarations.
Sure, thanks.
On Tue Aug 12 14:09:01 2025 +0000, Rémi Bernon wrote:
You're leaking event here.
Thanks.
On Tue Aug 12 14:09:00 2025 +0000, Rémi Bernon wrote:
It's usually preferred to keep the timeouts smaller if we're expecting a timeout, something like 10ms or 100ms instead should be good enough. It might not be as accurate but we want to avoid wasting time unnecessarily in the tests.
Yeah, I have shortened the timeouts, thanks.
On Tue Aug 12 14:08:58 2025 +0000, Rémi Bernon wrote:
I'd suggest to move these to the top, (and change the `span` variable name as it shadows the other one). When the todo_wine is going to be removed it would be nice to remove the if too.
Right, thanks.
On Tue Aug 12 14:08:43 2025 +0000, Rémi Bernon wrote:
contract(Windows.Foundation.UniversalApiContract, 19.0),
Thanks.