Skip to content

Commit fe95cbd

Browse files
committed
Add BluetoothApis to c.s.j.p.win32
Add mappings for the Windows Bluetooth API (BluetoothApis.dll): - BLUETOOTH_ADDRESS union (ullLong / rgBytes[6]) - BLUETOOTH_DEVICE_INFO struct - BLUETOOTH_DEVICE_SEARCH_PARAMS struct - BLUETOOTH_FIND_RADIO_PARAMS struct - BLUETOOTH_RADIO_INFO struct - BluetoothFindFirstRadio / BluetoothFindNextRadio / BluetoothFindRadioClose - BluetoothGetRadioInfo - BluetoothFindFirstDevice / BluetoothFindNextDevice / BluetoothFindDeviceClose
1 parent 5c421d7 commit fe95cbd

3 files changed

Lines changed: 366 additions & 0 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Features
1313
* [#1720](https://github.com/java-native-access/jna/pull/1720): Add `groupCount` and `groupMasks` fields to `CACHE_RELATIONSHIP` in `c.s.j.p.win32.WinNT`, matching the updated Windows struct layout - [@dbwiddis](https://github.com/dbwiddis).
1414
* [#1719](https://github.com/java-native-access/jna/pull/1719): Add `CoreGraphics` to `c.s.j.p.mac` with Quartz Window Services and Display Services bindings; implement `getAllWindows()` in `MacWindowUtils` - [@dbwiddis](https://github.com/dbwiddis).
1515
* [#1723](https://github.com/java-native-access/jna/pull/1723): Add `ProcFdInfo`, `InSockInfo`, `TcpSockInfo`, `proc_pidfdinfo`, `statfs64`, and `vm_deallocate` to `c.s.j.p.mac.SystemB` - [@dbwiddis](https://github.com/dbwiddis).
16+
* [#1725](https://github.com/java-native-access/jna/pull/1725): Add `BluetoothApis` to `c.s.j.p.win32` providing Bluetooth device and radio enumeration via `BluetoothFindFirstRadio`, `BluetoothFindFirstDevice`, and related functions - [@dbwiddis](https://github.com/dbwiddis).
1617

1718
Bug Fixes
1819
---------
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
/* Copyright (c) 2026 Daniel Widdis, All Rights Reserved
2+
*
3+
* The contents of this file is dual-licensed under 2
4+
* alternative Open Source/Free licenses: LGPL 2.1 or later and
5+
* Apache License 2.0. (starting with JNA version 4.0.0).
6+
*
7+
* You can freely decide which license you want to apply to
8+
* the project.
9+
*
10+
* You may obtain a copy of the LGPL License at:
11+
*
12+
* http://www.gnu.org/licenses/licenses.html
13+
*
14+
* A copy is also included in the downloadable source code package
15+
* containing JNA, in file "LGPL2.1".
16+
*
17+
* You may obtain a copy of the Apache License at:
18+
*
19+
* http://www.apache.org/licenses/
20+
*
21+
* A copy is also included in the downloadable source code package
22+
* containing JNA, in file "AL2.0".
23+
*/
24+
package com.sun.jna.platform.win32;
25+
26+
import com.sun.jna.Native;
27+
import com.sun.jna.Structure;
28+
import com.sun.jna.Structure.FieldOrder;
29+
import com.sun.jna.Union;
30+
import com.sun.jna.platform.win32.WinBase.SYSTEMTIME;
31+
import com.sun.jna.platform.win32.WinNT.HANDLE;
32+
import com.sun.jna.ptr.PointerByReference;
33+
import com.sun.jna.win32.StdCallLibrary;
34+
import com.sun.jna.win32.W32APIOptions;
35+
36+
/**
37+
* Provides mappings for the Windows Bluetooth API functions from {@code BluetoothApis.dll}.
38+
*
39+
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/">BluetoothApis.h</a>
40+
*/
41+
public interface BluetoothApis extends StdCallLibrary {
42+
43+
/** Instance of BluetoothApis. */
44+
BluetoothApis INSTANCE = Native.load("BluetoothApis", BluetoothApis.class, W32APIOptions.DEFAULT_OPTIONS);
45+
46+
/** Maximum Bluetooth device name length. */
47+
int BLUETOOTH_MAX_NAME_SIZE = 248;
48+
49+
/**
50+
* The {@code BLUETOOTH_ADDRESS} structure provides the address of a Bluetooth device. The address is stored as
51+
* either a {@code BTH_ADDR} (unsigned 64-bit integer) or a 6-byte array.
52+
*
53+
* @see <a href=
54+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/ns-bluetoothapis-bluetooth_address_struct">BLUETOOTH_ADDRESS</a>
55+
*/
56+
class BLUETOOTH_ADDRESS extends Union {
57+
/** The Bluetooth address as a 64-bit unsigned integer (only lower 48 bits used). */
58+
public long ullLong;
59+
/** The Bluetooth address as a 6-byte array in network byte order. */
60+
public byte[] rgBytes = new byte[6];
61+
62+
/**
63+
* Gets the address as a long.
64+
*
65+
* @return the address
66+
*/
67+
public long getAddress() {
68+
setType("ullLong");
69+
read();
70+
return ullLong;
71+
}
72+
73+
/**
74+
* Gets the address as a 6-byte array.
75+
*
76+
* @return the address bytes
77+
*/
78+
public byte[] getBytes() {
79+
setType("rgBytes");
80+
read();
81+
return rgBytes;
82+
}
83+
}
84+
85+
/**
86+
* The {@code BLUETOOTH_DEVICE_INFO} structure provides information about a Bluetooth device.
87+
*
88+
* @see <a href=
89+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/ns-bluetoothapis-bluetooth_device_info_struct">BLUETOOTH_DEVICE_INFO</a>
90+
*/
91+
@FieldOrder({ "dwSize", "Address", "ulClassofDevice", "fConnected", "fRemembered", "fAuthenticated", "stLastSeen",
92+
"stLastUsed", "szName" })
93+
class BLUETOOTH_DEVICE_INFO extends Structure {
94+
/** Size of the structure, in bytes. Must be set before calling any function. */
95+
public int dwSize;
96+
/** Address of the device. */
97+
public BLUETOOTH_ADDRESS Address;
98+
/** Class of Device (CoD) of the device. */
99+
public int ulClassofDevice;
100+
/** Whether the device is connected. */
101+
public boolean fConnected;
102+
/** Whether the device is a remembered device. Not all remembered devices are authenticated. */
103+
public boolean fRemembered;
104+
/** Whether the device is authenticated, paired, or bonded. All authenticated devices are remembered. */
105+
public boolean fAuthenticated;
106+
/** Last time the device was seen. */
107+
public SYSTEMTIME stLastSeen;
108+
/** Last time the device was used. */
109+
public SYSTEMTIME stLastUsed;
110+
/** Name of the device. */
111+
public char[] szName = new char[BLUETOOTH_MAX_NAME_SIZE];
112+
113+
/** Creates a new instance and sets {@code dwSize}. */
114+
public BLUETOOTH_DEVICE_INFO() {
115+
dwSize = size();
116+
}
117+
}
118+
119+
/**
120+
* The {@code BLUETOOTH_DEVICE_SEARCH_PARAMS} structure specifies search criteria for Bluetooth device searches.
121+
*
122+
* @see <a href=
123+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/ns-bluetoothapis-bluetooth_device_search_params">BLUETOOTH_DEVICE_SEARCH_PARAMS</a>
124+
*/
125+
@FieldOrder({ "dwSize", "fReturnAuthenticated", "fReturnRemembered", "fReturnUnknown", "fReturnConnected",
126+
"fIssueInquiry", "cTimeoutMultiplier", "hRadio" })
127+
class BLUETOOTH_DEVICE_SEARCH_PARAMS extends Structure {
128+
/** Size of the structure, in bytes. */
129+
public int dwSize;
130+
/** Whether to return authenticated devices. */
131+
public boolean fReturnAuthenticated;
132+
/** Whether to return remembered devices. */
133+
public boolean fReturnRemembered;
134+
/** Whether to return unknown devices. */
135+
public boolean fReturnUnknown;
136+
/** Whether to return connected devices. */
137+
public boolean fReturnConnected;
138+
/** Whether to issue a new inquiry. */
139+
public boolean fIssueInquiry;
140+
/** Timeout for the inquiry in increments of 1.28 seconds. Maximum value is 48. */
141+
public byte cTimeoutMultiplier;
142+
/** Handle to the radio on which to perform the inquiry. Set to {@code null} for all radios. */
143+
public HANDLE hRadio;
144+
145+
/** Creates a new instance and sets {@code dwSize}. */
146+
public BLUETOOTH_DEVICE_SEARCH_PARAMS() {
147+
dwSize = size();
148+
}
149+
}
150+
151+
/**
152+
* The {@code BLUETOOTH_FIND_RADIO_PARAMS} structure facilitates the enumeration of installed Bluetooth radios.
153+
*
154+
* @see <a href=
155+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/ns-bluetoothapis-bluetooth_find_radio_params">BLUETOOTH_FIND_RADIO_PARAMS</a>
156+
*/
157+
@FieldOrder({ "dwSize" })
158+
class BLUETOOTH_FIND_RADIO_PARAMS extends Structure {
159+
/** Size of the structure, in bytes. */
160+
public int dwSize;
161+
162+
/** Creates a new instance and sets {@code dwSize}. */
163+
public BLUETOOTH_FIND_RADIO_PARAMS() {
164+
dwSize = size();
165+
}
166+
}
167+
168+
/**
169+
* The {@code BLUETOOTH_RADIO_INFO} structure contains information about a Bluetooth radio.
170+
*
171+
* @see <a href=
172+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/ns-bluetoothapis-bluetooth_radio_info">BLUETOOTH_RADIO_INFO</a>
173+
*/
174+
@FieldOrder({ "dwSize", "address", "szName", "ulClassofDevice", "lmpSubversion", "manufacturer" })
175+
class BLUETOOTH_RADIO_INFO extends Structure {
176+
/** Size of the structure, in bytes. */
177+
public int dwSize;
178+
/** Address of the local Bluetooth radio. */
179+
public BLUETOOTH_ADDRESS address;
180+
/** Name of the local Bluetooth radio. */
181+
public char[] szName = new char[BLUETOOTH_MAX_NAME_SIZE];
182+
/** Class of Device for the local Bluetooth radio. */
183+
public int ulClassofDevice;
184+
/** Manufacturer-specific subversion data. */
185+
public short lmpSubversion;
186+
/** Manufacturer of the Bluetooth radio, expressed as a BTH_MFG_Xxx value. */
187+
public short manufacturer;
188+
189+
/** Creates a new instance and sets {@code dwSize}. */
190+
public BLUETOOTH_RADIO_INFO() {
191+
dwSize = size();
192+
}
193+
}
194+
195+
/**
196+
* Finds the first Bluetooth radio installed on the system.
197+
*
198+
* @param pbtfrp A pointer to a {@link BLUETOOTH_FIND_RADIO_PARAMS} structure.
199+
* @param phRadio A pointer that receives the handle of the first radio found.
200+
* @return A handle to use with {@link #BluetoothFindNextRadio} and {@link #BluetoothFindRadioClose}, or
201+
* {@code null} on failure. Call {@code GetLastError} for error information.
202+
* @see <a href=
203+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/nf-bluetoothapis-bluetoothfindfirstradio">BluetoothFindFirstRadio</a>
204+
*/
205+
HANDLE BluetoothFindFirstRadio(BLUETOOTH_FIND_RADIO_PARAMS pbtfrp, PointerByReference phRadio);
206+
207+
/**
208+
* Finds the next installed Bluetooth radio.
209+
*
210+
* @param hFind The handle returned by {@link #BluetoothFindFirstRadio}.
211+
* @param phRadio A pointer that receives the handle of the next radio found.
212+
* @return {@code true} if another radio was found, {@code false} otherwise.
213+
* @see <a href=
214+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/nf-bluetoothapis-bluetoothfindnextradio">BluetoothFindNextRadio</a>
215+
*/
216+
boolean BluetoothFindNextRadio(HANDLE hFind, PointerByReference phRadio);
217+
218+
/**
219+
* Closes the enumeration handle for Bluetooth radios.
220+
*
221+
* @param hFind The handle returned by {@link #BluetoothFindFirstRadio}.
222+
* @return {@code true} on success, {@code false} on failure.
223+
* @see <a href=
224+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/nf-bluetoothapis-bluetoothfindradioclose">BluetoothFindRadioClose</a>
225+
*/
226+
boolean BluetoothFindRadioClose(HANDLE hFind);
227+
228+
/**
229+
* Retrieves information about a Bluetooth radio.
230+
*
231+
* @param hRadio A handle to the Bluetooth radio obtained from {@link #BluetoothFindFirstRadio}.
232+
* @param pRadioInfo A pointer to a {@link BLUETOOTH_RADIO_INFO} structure to receive the radio information.
233+
* @return {@code ERROR_SUCCESS} (0) on success, or an error code on failure.
234+
* @see <a href=
235+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/nf-bluetoothapis-bluetoothgetradioinfo">BluetoothGetRadioInfo</a>
236+
*/
237+
int BluetoothGetRadioInfo(HANDLE hRadio, BLUETOOTH_RADIO_INFO pRadioInfo);
238+
239+
/**
240+
* Begins the enumeration of Bluetooth devices.
241+
*
242+
* @param pbtsp A pointer to a {@link BLUETOOTH_DEVICE_SEARCH_PARAMS} structure specifying search criteria.
243+
* @param pbtdi A pointer to a {@link BLUETOOTH_DEVICE_INFO} structure to receive the first device found.
244+
* @return A handle to use with {@link #BluetoothFindNextDevice} and {@link #BluetoothFindDeviceClose}, or
245+
* {@code null} if no devices are found.
246+
* @see <a href=
247+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/nf-bluetoothapis-bluetoothfindfirstdevice">BluetoothFindFirstDevice</a>
248+
*/
249+
HANDLE BluetoothFindFirstDevice(BLUETOOTH_DEVICE_SEARCH_PARAMS pbtsp, BLUETOOTH_DEVICE_INFO pbtdi);
250+
251+
/**
252+
* Finds the next Bluetooth device.
253+
*
254+
* @param hFind The handle returned by {@link #BluetoothFindFirstDevice}.
255+
* @param pbtdi A pointer to a {@link BLUETOOTH_DEVICE_INFO} structure to receive the next device found.
256+
* @return {@code true} if another device was found, {@code false} otherwise.
257+
* @see <a href=
258+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/nf-bluetoothapis-bluetoothfindnextdevice">BluetoothFindNextDevice</a>
259+
*/
260+
boolean BluetoothFindNextDevice(HANDLE hFind, BLUETOOTH_DEVICE_INFO pbtdi);
261+
262+
/**
263+
* Closes the enumeration handle for Bluetooth devices.
264+
*
265+
* @param hFind The handle returned by {@link #BluetoothFindFirstDevice}.
266+
* @return {@code true} on success, {@code false} on failure.
267+
* @see <a href=
268+
* "https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/nf-bluetoothapis-bluetoothfinddeviceclose">BluetoothFindDeviceClose</a>
269+
*/
270+
boolean BluetoothFindDeviceClose(HANDLE hFind);
271+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/* Copyright (c) 2026 Daniel Widdis, All Rights Reserved
2+
*
3+
* The contents of this file is dual-licensed under 2
4+
* alternative Open Source/Free licenses: LGPL 2.1 or later and
5+
* Apache License 2.0. (starting with JNA version 4.0.0).
6+
*
7+
* You can freely decide which license you want to apply to
8+
* the project.
9+
*
10+
* You may obtain a copy of the LGPL License at:
11+
*
12+
* http://www.gnu.org/licenses/licenses.html
13+
*
14+
* A copy is also included in the downloadable source code package
15+
* containing JNA, in file "LGPL2.1".
16+
*
17+
* You may obtain a copy of the Apache License at:
18+
*
19+
* http://www.apache.org/licenses/
20+
*
21+
* A copy is also included in the downloadable source code package
22+
* containing JNA, in file "AL2.0".
23+
*/
24+
package com.sun.jna.platform.win32;
25+
26+
import static org.junit.Assert.assertNotNull;
27+
import static org.junit.Assert.assertTrue;
28+
29+
import org.junit.Test;
30+
31+
import com.sun.jna.platform.win32.BluetoothApis.BLUETOOTH_DEVICE_INFO;
32+
import com.sun.jna.platform.win32.BluetoothApis.BLUETOOTH_DEVICE_SEARCH_PARAMS;
33+
import com.sun.jna.platform.win32.BluetoothApis.BLUETOOTH_FIND_RADIO_PARAMS;
34+
import com.sun.jna.platform.win32.BluetoothApis.BLUETOOTH_RADIO_INFO;
35+
import com.sun.jna.platform.win32.WinNT.HANDLE;
36+
import com.sun.jna.ptr.PointerByReference;
37+
38+
/**
39+
* Tests for {@link BluetoothApis}.
40+
*/
41+
public class BluetoothApisTest {
42+
43+
@Test
44+
public void testStructureSizes() {
45+
BLUETOOTH_FIND_RADIO_PARAMS radioParams = new BLUETOOTH_FIND_RADIO_PARAMS();
46+
assertTrue("BLUETOOTH_FIND_RADIO_PARAMS size should be at least 4", radioParams.dwSize >= 4);
47+
48+
BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = new BLUETOOTH_DEVICE_SEARCH_PARAMS();
49+
assertTrue("BLUETOOTH_DEVICE_SEARCH_PARAMS size should be at least 32", searchParams.dwSize >= 32);
50+
51+
BLUETOOTH_DEVICE_INFO deviceInfo = new BLUETOOTH_DEVICE_INFO();
52+
assertTrue("BLUETOOTH_DEVICE_INFO size should be at least 560", deviceInfo.dwSize >= 560);
53+
54+
BLUETOOTH_RADIO_INFO radioInfo = new BLUETOOTH_RADIO_INFO();
55+
assertTrue("BLUETOOTH_RADIO_INFO size should be at least 520", radioInfo.dwSize >= 520);
56+
}
57+
58+
@Test
59+
public void testBluetoothFindFirstRadio() {
60+
BLUETOOTH_FIND_RADIO_PARAMS radioParams = new BLUETOOTH_FIND_RADIO_PARAMS();
61+
PointerByReference phRadio = new PointerByReference();
62+
63+
// This may return null if no Bluetooth radio is present, which is acceptable
64+
HANDLE hFind = BluetoothApis.INSTANCE.BluetoothFindFirstRadio(radioParams, phRadio);
65+
if (hFind != null) {
66+
// If a radio was found, enumerate devices on it
67+
HANDLE hRadio = new HANDLE(phRadio.getValue());
68+
assertNotNull("Radio handle should not be null", hRadio);
69+
70+
BLUETOOTH_RADIO_INFO radioInfo = new BLUETOOTH_RADIO_INFO();
71+
int result = BluetoothApis.INSTANCE.BluetoothGetRadioInfo(hRadio, radioInfo);
72+
assertTrue("BluetoothGetRadioInfo should succeed", result == 0);
73+
74+
// Enumerate devices
75+
BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = new BLUETOOTH_DEVICE_SEARCH_PARAMS();
76+
searchParams.fReturnAuthenticated = true;
77+
searchParams.fReturnRemembered = true;
78+
searchParams.fReturnConnected = true;
79+
searchParams.fReturnUnknown = false;
80+
searchParams.fIssueInquiry = false;
81+
searchParams.cTimeoutMultiplier = 0;
82+
searchParams.hRadio = hRadio;
83+
84+
BLUETOOTH_DEVICE_INFO deviceInfo = new BLUETOOTH_DEVICE_INFO();
85+
HANDLE hFindDevice = BluetoothApis.INSTANCE.BluetoothFindFirstDevice(searchParams, deviceInfo);
86+
if (hFindDevice != null) {
87+
BluetoothApis.INSTANCE.BluetoothFindDeviceClose(hFindDevice);
88+
}
89+
90+
Kernel32.INSTANCE.CloseHandle(hRadio);
91+
BluetoothApis.INSTANCE.BluetoothFindRadioClose(hFind);
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)