Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
DBTScanner10.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 * Copyright (c) 2020 ZAFENA AB
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26import java.lang.reflect.InvocationTargetException;
27import java.util.Iterator;
28import java.util.List;
29import java.util.concurrent.atomic.AtomicInteger;
30import java.util.function.Consumer;
31
32import org.direct_bt.AdapterSettings;
33import org.direct_bt.AdapterStatusListener;
34import org.direct_bt.BTMode;
35import org.direct_bt.BTSecurityLevel;
36import org.direct_bt.BTAdapter;
37import org.direct_bt.BTDevice;
38import org.direct_bt.BTDeviceRegistry;
39import org.direct_bt.BTException;
40import org.direct_bt.BTFactory;
41import org.direct_bt.BTGattChar;
42import org.direct_bt.BTGattCharListener;
43import org.direct_bt.BTGattCmd;
44import org.direct_bt.BTGattDesc;
45import org.direct_bt.BTGattService;
46import org.direct_bt.BTManager;
47import org.direct_bt.BTSecurityRegistry;
48import org.direct_bt.BTUtils;
49import org.direct_bt.DiscoveryPolicy;
50import org.direct_bt.EIRDataTypeSet;
51import org.direct_bt.EInfoReport;
52import org.direct_bt.GattCharPropertySet;
53import org.direct_bt.HCIStatusCode;
54import org.direct_bt.LE_Features;
55import org.direct_bt.LE_PHYs;
56import org.direct_bt.PairingMode;
57import org.direct_bt.SMPIOCapability;
58import org.direct_bt.SMPKeyBin;
59import org.direct_bt.SMPPairingState;
60import org.direct_bt.ScanType;
61import org.jau.io.PrintUtil;
62import org.jau.net.EUI48;
63import org.jau.sys.Clock;
64import org.jau.util.BasicTypes;
65
66import jau.direct_bt.DBTManager;
67
68/**
69 * This Java scanner {@link BTRole::Master} GATT client example uses an event driven workflow
70 * and multithreading, i.e. one thread processes each found device when notified.
71 * <p>
72 * This example represents the recommended utilization of Direct-BT.
73 * </p>
74 * <p>
75 * See `dbt_scanner10.cpp` for invocation examples, since both apps are fully compatible.
76 * </p>
77 */
78public class DBTScanner10 {
79 long timestamp_t0;
80
81 EUI48 useAdapter = EUI48.ALL_DEVICE;
82 BTMode btMode = BTMode.DUAL;
83 BTAdapter chosenAdapter = null;
84
85 int RESET_ADAPTER_EACH_CONN = 0;
86 AtomicInteger deviceReadyCount = new AtomicInteger(0);
87
88 AtomicInteger MULTI_MEASUREMENTS = new AtomicInteger(8);
89 boolean KEEP_CONNECTED = true;
90
91 boolean GATT_PING_ENABLED = false;
92 boolean REMOVE_DEVICE = true;
93
94 // Default from dbt_peripheral00.cpp or DBTPeripheral00.java
95 String cmd_uuid = "d0ca6bf3-3d52-4760-98e5-fc5883e93712";
96 String cmd_rsp_uuid = "d0ca6bf3-3d53-4760-98e5-fc5883e93712";
97 byte cmd_arg = (byte)0x44;
98
99 boolean SHOW_UPDATE_EVENTS = false;
100 boolean QUIET = false;
101
102 int shutdownTest = 0;
103
104 static void executeOffThread(final Runnable runobj, final String threadName, final boolean detach) {
105 final Thread t = new Thread( runobj, threadName );
106 if( detach ) {
107 t.setDaemon(true); // detach thread
108 }
109 t.start();
110 }
111 static void execute(final Runnable task, final boolean offThread) {
112 if( offThread ) {
113 final Thread t = new Thread(task);
114 t.setDaemon(true);
115 t.start();
116 } else {
117 task.run();
118 }
119 }
120
121 class MyAdapterStatusListener extends AdapterStatusListener {
122 @Override
123 public void adapterSettingsChanged(final BTAdapter adapter, final AdapterSettings oldmask,
124 final AdapterSettings newmask, final AdapterSettings changedmask, final long timestamp) {
125 final boolean initialSetting = oldmask.isEmpty();
126 if( initialSetting ) {
127 PrintUtil.println(System.err, "****** SETTINGS: "+oldmask+" -> "+newmask+", initial "+changedmask);
128 } else {
129 PrintUtil.println(System.err, "****** SETTINGS: "+oldmask+" -> "+newmask+", changed "+changedmask);
130 }
131 PrintUtil.println(System.err, "Status Adapter:");
132 PrintUtil.println(System.err, adapter.toString());
133 if( !initialSetting &&
136 {
137
138 executeOffThread( () -> { startDiscovery(adapter, "powered-on"); },
139 "DBT-StartDiscovery-"+adapter.getAddressAndType(), true /* detach */);
140 }
141 }
142
143 @Override
144 public void discoveringChanged(final BTAdapter adapter, final ScanType currentMeta, final ScanType changedType, final boolean changedEnabled, final DiscoveryPolicy policy, final long timestamp) {
145 PrintUtil.println(System.err, "****** DISCOVERING: meta "+currentMeta+", changed["+changedType+", enabled "+changedEnabled+", policy "+policy+"] on "+adapter);
146 }
147
148 @Override
149 public boolean deviceFound(final BTDevice device, final long timestamp) {
152 ( 0 < MULTI_MEASUREMENTS.get() || !BTDeviceRegistry.isDeviceProcessed(device.getAddressAndType()) )
153 )
154 )
155 {
156 PrintUtil.println(System.err, "****** FOUND__-0: Connecting "+device.toString());
157 {
158 final long td = Clock.currentTimeMillis() - timestamp_t0; // adapter-init -> now
159 PrintUtil.println(System.err, "PERF: adapter-init -> FOUND__-0 " + td + " ms");
160 }
161 executeOffThread( () -> { connectDiscoveredDevice(device); },
162 "DBT-Connect-"+device.getAddressAndType(), true /* detach */);
163 return true;
164 } else {
165 if( !QUIET ) {
166 PrintUtil.println(System.err, "****** FOUND__-1: NOP "+device.toString());
167 }
168 return false;
169 }
170 }
171
172 @Override
173 public void deviceUpdated(final BTDevice device, final EIRDataTypeSet updateMask, final long timestamp) {
174 if( !QUIET && SHOW_UPDATE_EVENTS ) {
175 PrintUtil.println(System.err, "****** UPDATED: "+updateMask+" of "+device);
176 }
177 }
178
179 @Override
180 public void deviceConnected(final BTDevice device, final boolean discovered, final long timestamp) {
181 PrintUtil.println(System.err, "****** CONNECTED (discovered "+discovered+"): "+device.toString());
182 }
183
184 @Override
185 public void devicePairingState(final BTDevice device, final SMPPairingState state, final PairingMode mode, final long timestamp) {
186 PrintUtil.println(System.err, "****** PAIRING_STATE: state "+state+", mode "+mode+": "+device);
187 switch( state ) {
188 case NONE:
189 // next: deviceReady(..)
190 break;
191 case FAILED: {
192 final boolean res = SMPKeyBin.remove(DBTConstants.CLIENT_KEY_PATH, device);
193 PrintUtil.println(System.err, "****** PAIRING_STATE: state "+state+"; Remove key file "+SMPKeyBin.getFilename(DBTConstants.CLIENT_KEY_PATH, device)+", res "+res);
194 // next: deviceReady() or deviceDisconnected(..)
195 } break;
196 case REQUESTED_BY_RESPONDER:
197 // next: FEATURE_EXCHANGE_STARTED
198 break;
199 case FEATURE_EXCHANGE_STARTED:
200 // next: FEATURE_EXCHANGE_COMPLETED
201 break;
202 case FEATURE_EXCHANGE_COMPLETED:
203 // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION
204 break;
205 case PASSKEY_EXPECTED: {
206 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, device.getName());
207 if( null != sec && sec.getPairingPasskey() != BTSecurityRegistry.NO_PASSKEY ) {
208 executeOffThread( () -> { device.setPairingPasskey( sec.getPairingPasskey() ); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */);
209 } else {
210 executeOffThread( () -> { device.setPairingPasskey( 0 ); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */);
211 // 3s disconnect: executeOffThread( () -> { device.setPairingPasskeyNegative(); }, "DBT-SetPasskeyNegative-"+device.getAddressAndType(), true /* detach */);
212 }
213 // next: KEY_DISTRIBUTION or FAILED
214 } break;
215 case NUMERIC_COMPARE_EXPECTED: {
216 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, device.getName());
217 if( null != sec ) {
218 executeOffThread( () -> { device.setPairingNumericComparison( sec.getPairingNumericComparison() ); }, "DBT-SetNumericComp-"+device.getAddressAndType(), true /* detach */);
219 } else {
220 executeOffThread( () -> { device.setPairingNumericComparison( false ); }, "DBT-SetNumericCompFalse-"+device.getAddressAndType(), true /* detach */);
221 }
222 // next: KEY_DISTRIBUTION or FAILED
223 } break;
224 case OOB_EXPECTED:
225 // FIXME: ABORT
226 break;
227 case KEY_DISTRIBUTION:
228 // next: COMPLETED or FAILED
229 break;
230 case COMPLETED:
231 // next: deviceReady(..)
232 break;
233 default: // nop
234 break;
235 }
236 }
237
238 @Override
239 public void deviceReady(final BTDevice device, final long timestamp) {
240 deviceReadyCount.incrementAndGet();
241 PrintUtil.println(System.err, "****** READY-0: Processing["+deviceReadyCount.get()+"] "+device.toString());
242 {
243 final long td = Clock.currentTimeMillis() - timestamp_t0; // adapter-init -> now
244 PrintUtil.println(System.err, "PERF: adapter-init -> READY-0 " + td + " ms");
245 }
246 processReadyDevice(device); // AdapterStatusListener::deviceReady() explicitly allows prolonged and complex code execution!
247 }
248
249 @Override
250 public void deviceDisconnected(final BTDevice device, final HCIStatusCode reason, final short handle, final long timestamp) {
251 PrintUtil.println(System.err, "****** DISCONNECTED: Reason "+reason+", old handle 0x"+Integer.toHexString(handle)+": "+device+" on "+device.getAdapter());
252
253 if( REMOVE_DEVICE ) {
254 executeOffThread( () -> { removeDevice(device); }, "DBT-Remove-"+device.getAddressAndType(), true /* detach */);
255 }
256 if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount.get() % RESET_ADAPTER_EACH_CONN ) {
257 executeOffThread( () -> { resetAdapter(device.getAdapter(), 1); },
258 "DBT-Reset-"+device.getAdapter().getAddressAndType(), true /* detach */ );
259 }
260 }
261
262 @Override
263 public String toString() {
264 return "AdapterStatusListener[user, per-adapter]";
265 }
266 };
267
268 class MyGATTEventListener extends BTGattCharListener {
269 private final int i, j;
270
271 public MyGATTEventListener(final int i_, final int j_) { i=i_; j=j_; }
272
273 @Override
274 public void notificationReceived(final BTGattChar charDecl,
275 final byte[] value, final long timestamp) {
276 final long tR = Clock.currentTimeMillis();
277 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Characteristic-Notify: UUID %s, td %d ******\n",
278 i, j, charDecl.getUUID(), (tR-timestamp));
279 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString());
280 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BasicTypes.bytesHexString(value, 0, -1, true));
281 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Value S: %s ******\n", i, j, BTUtils.decodeUTF8String(value, 0, value.length));
282
283 shutdownTest();
284 }
285
286 @Override
287 public void indicationReceived(final BTGattChar charDecl,
288 final byte[] value, final long timestamp, final boolean confirmationSent) {
289 final long tR = Clock.currentTimeMillis();
290 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Characteristic-Indication: UUID %s, td %d, confirmed %b ******\n",
291 i, j, charDecl.getUUID(), (tR-timestamp), confirmationSent);
292 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString());
293 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BasicTypes.bytesHexString(value, 0, -1, true));
294 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Value S: %s ******\n", i, j, BTUtils.decodeUTF8String(value, 0, value.length));
295
296 shutdownTest();
297 }
298 }
299
300 private void connectDiscoveredDevice(final BTDevice device) {
301 PrintUtil.println(System.err, "****** Connecting Device: Start " + device.toString());
302
303 // Testing listener lifecycle @ device dtor
305 @Override
306 public void deviceUpdated(final BTDevice device, final EIRDataTypeSet updateMask, final long timestamp) {
307 if( SHOW_UPDATE_EVENTS ) {
308 PrintUtil.println(System.err, "****** UPDATED(2): "+updateMask+" of "+device);
309 }
310 }
311 @Override
312 public void deviceConnected(final BTDevice device, final boolean discovered, final long timestamp) {
313 PrintUtil.println(System.err, "****** CONNECTED(2) (discovered "+discovered+"): "+device.toString());
314 }
315
316 @Override
317 public String toString() {
318 return "TempAdapterStatusListener[user, device "+device.getAddressAndType().toString()+"]";
319 } });
320
321 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, device.getName());
322 if( null != sec ) {
323 PrintUtil.println(System.err, "****** Connecting Device: Found SecurityDetail "+sec.toString()+" for "+device.toString());
324 } else {
325 PrintUtil.println(System.err, "****** Connecting Device: No SecurityDetail for "+device.toString());
326 }
327 final BTSecurityLevel req_sec_level = null != sec ? sec.getSecLevel() : BTSecurityLevel.UNSET;
328 HCIStatusCode res = device.uploadKeys(DBTConstants.CLIENT_KEY_PATH, req_sec_level, true /* verbose_ */);
329 PrintUtil.fprintf_td(System.err, "****** Connecting Device: BTDevice::uploadKeys(...) result %s\n", res.toString());
330 if( HCIStatusCode.SUCCESS != res ) {
331 if( null != sec ) {
332 if( sec.isSecurityAutoEnabled() ) {
333 final boolean r = device.setConnSecurityAuto( sec.getSecurityAutoIOCap() );
334 PrintUtil.println(System.err, "****** Connecting Device: Using SecurityDetail.SEC AUTO "+sec+" -> set OK "+r);
335 } else if( sec.isSecLevelOrIOCapSet() ) {
336 final boolean r = device.setConnSecurity(sec.getSecLevel(), sec.getIOCap());
337 PrintUtil.println(System.err, "****** Connecting Device: Using SecurityDetail.Level+IOCap "+sec+" -> set OK "+r);
338 } else {
339 final boolean r = device.setConnSecurityAuto( SMPIOCapability.KEYBOARD_ONLY );
340 PrintUtil.println(System.err, "****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY ("+sec+") -> set OK "+r);
341 }
342 } else {
343 final boolean r = device.setConnSecurityAuto( SMPIOCapability.KEYBOARD_ONLY );
344 PrintUtil.println(System.err, "****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY -> set OK "+r);
345 }
346 }
347 final EInfoReport eir = device.getEIR();
348 PrintUtil.println(System.err, "EIR-1 "+device.getEIRInd().toString());
349 PrintUtil.println(System.err, "EIR-2 "+device.getEIRScanRsp().toString());
350 PrintUtil.println(System.err, "EIR-+ "+eir.toString());
351
352 short conn_interval_min = (short)8; // 10ms
353 short conn_interval_max = (short)12; // 15ms
354 final short conn_latency = (short)0;
356 final short[] minmax = new short[2];
357 eir.getConnInterval(minmax);
358 conn_interval_min = minmax[0];
359 conn_interval_max = minmax[1];
360 }
361 final short supervision_timeout = BTUtils.getHCIConnSupervisorTimeout(conn_latency, (int) ( conn_interval_max * 1.25 ) /* ms */);
362 res = device.connectLE(le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max, conn_latency, supervision_timeout);
363 PrintUtil.println(System.err, "****** Connecting Device Command, res "+res+": End result "+res+" of " + device.toString());
364 }
365
366 void shutdownTest() {
367 switch( shutdownTest ) {
368 case 1:
369 shutdownTest01();
370 break;
371 case 2:
372 shutdownTest02();
373 break;
374 default:
375 // nop
376 }
377 }
378 void shutdownTest01() {
379 execute( () -> {
381 mngr.shutdown();
382 }, true);
383 }
384 void shutdownTest02() {
385 execute( () -> {
386 System.exit(1);
387 }, true);
388 }
389
390 private void processReadyDevice(final BTDevice device) {
391 PrintUtil.println(System.err, "****** Processing Ready Device: Start " + device.toString());
392 final long t1 = Clock.currentTimeMillis();
393
394 SMPKeyBin.createAndWrite(device, DBTConstants.CLIENT_KEY_PATH, true /* verbose */);
395 final long t2 = Clock.currentTimeMillis();
396
397 boolean success = false;
398
399 if( device.getAdapter().getBTMajorVersion() > 4 ) {
400 final LE_PHYs Tx = new LE_PHYs(LE_PHYs.PHY.LE_2M);
401 final LE_PHYs Rx = new LE_PHYs(LE_PHYs.PHY.LE_2M);
402
403 final HCIStatusCode res = device.setConnectedLE_PHY(Tx, Rx);
404 PrintUtil.fprintf_td(System.err, "****** Set Connected LE PHY: status %s: Tx %s, Rx %s\n",
405 res.toString(), Tx.toString(), Rx.toString());
406 }
407 {
408 final LE_PHYs resTx[] = { new LE_PHYs() };
409 final LE_PHYs resRx[] = { new LE_PHYs() };
410 final HCIStatusCode res = device.getConnectedLE_PHY(resTx, resRx);
411 PrintUtil.fprintf_td(System.err, "****** Got Connected LE PHY: status %s: Tx %s, Rx %s\n",
412 res.toString(), resTx[0].toString(), resRx[0].toString());
413 }
414 final long t3 = Clock.currentTimeMillis();
415
416 //
417 // GATT Service Processing
418 //
419 try {
420 final List<BTGattService> primServices = device.getGattServices();
421 if( null == primServices || 0 == primServices.size() ) {
422 // Cheating the flow, but avoiding: goto, do-while-false and lastly unreadable intendations
423 // And it is an error case nonetheless ;-)
424 throw new RuntimeException("Processing Ready Device: getServices() failed " + device.toString());
425 }
426 final long t5 = Clock.currentTimeMillis();
427 {
428 final long td00 = device.getLastDiscoveryTimestamp() - timestamp_t0; // adapter-init to discovered
429 final long td01 = t1 - timestamp_t0; // adapter-init to processing-start
430 final long td05 = t5 - timestamp_t0; // adapter-init -> gatt-complete
431 final long tdc1 = t1 - device.getLastDiscoveryTimestamp(); // discovered to processing-start
432 final long tdc5 = t5 - device.getLastDiscoveryTimestamp(); // discovered to gatt-complete
433 final long td12 = t2 - t1; // SMPKeyBin
434 final long td23 = t3 - t2; // LE_PHY
435 final long td13 = t3 - t1; // SMPKeyBin + LE_PHY
436 final long td35 = t5 - t3; // get-gatt-services
437 PrintUtil.println(System.err, System.lineSeparator()+System.lineSeparator());
438 PrintUtil.println(System.err, "PERF: GATT primary-services completed"+System.lineSeparator()+
439 "PERF: adapter-init to discovered " + td00 + " ms,"+System.lineSeparator()+
440 "PERF: adapter-init to processing-start " + td01 + " ms,"+System.lineSeparator()+
441 "PERF: adapter-init to gatt-complete " + td05 + " ms,"+System.lineSeparator()+
442 "PERF: discovered to processing-start " + tdc1 + " ms,"+System.lineSeparator()+
443 "PERF: discovered to gatt-complete " + tdc5 + " ms,"+System.lineSeparator()+
444 "PERF: SMPKeyBin + LE_PHY " + td13 + " ms (SMPKeyBin "+td12+"ms, LE_PHY "+td23+"ms), "+System.lineSeparator()+
445 "PERF: get-gatt-services " + td35 + " ms,"+System.lineSeparator());
446 }
447
448 if( null != cmd_uuid ) {
449 final BTGattCmd cmd = null != cmd_rsp_uuid ? new BTGattCmd(device, "TestCmd", null /* service_uuid */, cmd_uuid, cmd_rsp_uuid)
450 : new BTGattCmd(device, "TestCmd", null /* service_uuid */, cmd_uuid);
451 cmd.setVerbose(true);
452 final boolean cmd_resolved = cmd.isResolved();
453 PrintUtil.println(System.err, "Command test: "+cmd.toString()+", resolved "+cmd_resolved);
454 final byte[] cmd_data = { cmd_arg };
455 final HCIStatusCode cmd_res = cmd.send(true /* prefNoAck */, cmd_data, 3000 /* timeoutMS */);
456 if( HCIStatusCode.SUCCESS == cmd_res ) {
457 if( cmd.hasResponseSet() ) {
458 final byte[] resp = cmd.getResponse();
459 if( 1 == resp.length && resp[0] == cmd_arg ) {
460 PrintUtil.fprintf_td(System.err, "Success: %s -> %s (echo response)\n", cmd.toString(), BasicTypes.bytesHexString(resp, 0, resp.length, true /* lsb */));
461 } else {
462 PrintUtil.fprintf_td(System.err, "Success: %s -> %s (different response)\n", cmd.toString(), BasicTypes.bytesHexString(resp, 0, resp.length, true /* lsb */));
463 }
464 } else {
465 PrintUtil.fprintf_td(System.err, "Success: %s -> no response\n", cmd.toString());
466 }
467 } else {
468 PrintUtil.fprintf_td(System.err, "Failure: %s -> %s\n", cmd.toString(), cmd_res.toString());
469 }
470 cmd.close();
471 }
472
473 try {
474 int i=0;
475 for(final Iterator<BTGattService> srvIter = primServices.iterator(); srvIter.hasNext(); i++) {
476 final BTGattService primService = srvIter.next();
477 {
478 PrintUtil.fprintf_td(System.err, " [%02d] Service UUID %s\n", i, primService.getUUID());
479 PrintUtil.fprintf_td(System.err, " [%02d] %s\n", i, primService.toString());
480 }
481 int j=0;
482 final List<BTGattChar> serviceCharacteristics = primService.getChars();
483 for(final Iterator<BTGattChar> charIter = serviceCharacteristics.iterator(); charIter.hasNext(); j++) {
484 final BTGattChar serviceChar = charIter.next();
485 {
486 PrintUtil.fprintf_td(System.err, " [%02d.%02d] Characteristic: UUID %s\n", i, j, serviceChar.getUUID());
487 PrintUtil.fprintf_td(System.err, " [%02d.%02d] %s\n", i, j, serviceChar.toString());
488 }
489 final GattCharPropertySet properties = serviceChar.getProperties();
490 if( properties.isSet(GattCharPropertySet.Type.Read) ) {
491 final byte[] value = serviceChar.readValue();
492 final String svalue = BTUtils.decodeUTF8String(value, 0, value.length);
493 {
494 PrintUtil.fprintf_td(System.err, " [%02d.%02d] value: %s ('%s')\n", i, j, BasicTypes.bytesHexString(value, 0, -1, true), svalue);
495 }
496 }
497 int k=0;
498 final List<BTGattDesc> charDescList = serviceChar.getDescriptors();
499 for(final Iterator<BTGattDesc> descIter = charDescList.iterator(); descIter.hasNext(); k++) {
500 final BTGattDesc charDesc = descIter.next();
501 {
502 PrintUtil.fprintf_td(System.err, " [%02d.%02d.%02d] Descriptor: UUID %s\n", i, j, k, charDesc.getUUID());
503 PrintUtil.fprintf_td(System.err, " [%02d.%02d.%02d] %s\n", i, j, k, charDesc.toString());
504 }
505 }
506 final boolean cccdEnableResult[] = { false, false };
507 if( serviceChar.enableNotificationOrIndication( cccdEnableResult ) ) {
508 // ClientCharConfigDescriptor (CCD) is available
509 final boolean clAdded = serviceChar.addCharListener( new MyGATTEventListener(i, j) );
510 {
511 PrintUtil.fprintf_td(System.err, " [%02d.%02d] Characteristic-Listener: Notification(%b), Indication(%b): Added %b\n",
512 i, j, cccdEnableResult[0], cccdEnableResult[1], clAdded);
513 PrintUtil.fprintf_td(System.err, "\n");
514 }
515 }
516 }
517 PrintUtil.fprintf_td(System.err, "\n");
518 }
519 } catch( final Exception ex) {
520 PrintUtil.println(System.err, "Caught "+ex.getMessage());
521 ex.printStackTrace();
522 }
523 // FIXME sleep 1s for potential callbacks ..
524 try {
525 Thread.sleep(1000);
526 } catch (final InterruptedException e) {
527 e.printStackTrace();
528 }
529 success = true;
530 } catch (final Throwable t ) {
531 PrintUtil.println(System.err, "****** Processing Ready Device: Exception caught for " + device.toString() + ": "+t.getMessage());
532 t.printStackTrace();
533 }
534
535 PrintUtil.println(System.err, "****** Processing Ready Device: End-1: Success " + success + " on " + device.toString());
536
537 if( DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_DISCONNECTED == discoveryPolicy ) {
539 }
540
541 if( KEEP_CONNECTED && GATT_PING_ENABLED && success ) {
542 while( device.pingGATT() ) {
543 PrintUtil.println(System.err, "****** Processing Ready Device: pingGATT OK: "+device.getAddressAndType());
544 try {
545 Thread.sleep(1000);
546 } catch (final InterruptedException e) {
547 e.printStackTrace();
548 }
549 }
550 PrintUtil.println(System.err, "****** Processing Ready Device: pingGATT failed, waiting for disconnect: "+device.getAddressAndType());
551 // Even w/ GATT_PING_ENABLED, we utilize disconnect event to clean up -> remove
552 }
553
554 PrintUtil.println(System.err, "****** Processing Ready Device: End-2: Success " + success + " on " + device.toString());
555 if( success ) {
557 }
558 device.removeAllCharListener();
559
560 if( !KEEP_CONNECTED ) {
561
562 device.remove();
563
564 if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount.get() % RESET_ADAPTER_EACH_CONN ) {
565 resetAdapter(device.getAdapter(), 2);
566 }
567 }
568
569 if( 0 < MULTI_MEASUREMENTS.get() ) {
570 MULTI_MEASUREMENTS.decrementAndGet();
571 PrintUtil.println(System.err, "****** Processing Ready Device: MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS.get()+": "+device.getAddressAndType());
572 }
573 }
574
575 private void removeDevice(final BTDevice device) {
576 PrintUtil.println(System.err, "****** Remove Device: removing: "+device.getAddressAndType());
577
578 device.remove();
579 }
580
581 private void resetAdapter(final BTAdapter adapter, final int mode) {
582 PrintUtil.println(System.err, "****** Reset Adapter: reset["+mode+"] start: "+adapter.toString());
583 final HCIStatusCode res = adapter.reset();
584 PrintUtil.println(System.err, "****** Reset Adapter: reset["+mode+"] end: "+res+", "+adapter.toString());
585 }
586
587 DiscoveryPolicy discoveryPolicy = DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_READY; // default value
588 boolean le_scan_active = true; // default value
589 static final short le_scan_interval = (short)24; // default value
590 static final short le_scan_window = (short)24; // default value
591 static final byte filter_policy = (byte)0; // default value
592 static final boolean filter_dup = true; // default value
593
594 private boolean startDiscovery(final BTAdapter adapter, final String msg) {
595 if( !useAdapter.equals(EUI48.ALL_DEVICE) && !useAdapter.equals(adapter.getAddressAndType().address) ) {
596 PrintUtil.fprintf_td(System.err, "****** Start discovery (%s): Adapter not selected: %s\n", msg, adapter.toString());
597 return false;
598 }
599 final HCIStatusCode status = adapter.startDiscovery(null, discoveryPolicy, le_scan_active, le_scan_interval, le_scan_window, filter_policy, filter_dup );
600 PrintUtil.println(System.err, "****** Start discovery ("+msg+") result: "+status);
601 return HCIStatusCode.SUCCESS == status;
602 }
603
604 private boolean initAdapter(final BTAdapter adapter) {
605 if( !useAdapter.equals(EUI48.ALL_DEVICE) && !useAdapter.equals(adapter.getAddressAndType().address) ) {
606 PrintUtil.fprintf_td(System.err, "initAdapter: Adapter not selected: %s\n", adapter.toString());
607 return false;
608 }
609 // Initialize with defaults and power-on
610 if( !adapter.isInitialized() ) {
611 final HCIStatusCode status = adapter.initialize( btMode, true );
612 if( HCIStatusCode.SUCCESS != status ) {
613 PrintUtil.fprintf_td(System.err, "initAdapter: Adapter initialization failed: %s: %s\n",
614 status.toString(), adapter.toString());
615 return false;
616 }
617 } else if( !adapter.setPowered( true ) ) {
618 PrintUtil.fprintf_td(System.err, "initAdapter: Already initialized adapter power-on failed:: %s\n", adapter.toString());
619 return false;
620 }
621 // adapter is powered-on
622 PrintUtil.fprintf_td(System.err, "initAdapter: %s\n", adapter.toString());
623 {
624 final LE_Features le_feats = adapter.getLEFeatures();
625 PrintUtil.fprintf_td(System.err, "initAdapter: LE_Features %s\n", le_feats.toString());
626 }
627 if( adapter.getBTMajorVersion() > 4 ) {
628 final LE_PHYs Tx = new LE_PHYs(LE_PHYs.PHY.LE_2M);
629 final LE_PHYs Rx = new LE_PHYs(LE_PHYs.PHY.LE_2M);
630
631 final HCIStatusCode res = adapter.setDefaultLE_PHY(Tx, Rx);
632 PrintUtil.fprintf_td(System.err, "initAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n",
633 res.toString(), Tx.toString(), Rx.toString());
634 }
635 final AdapterStatusListener asl = new MyAdapterStatusListener();
636 adapter.addStatusListener( asl );
637
638 if( !startDiscovery(adapter, "initAdapter") ) {
639 adapter.removeStatusListener( asl );
640 return false;
641 }
642 return true;
643 }
644
645 private final BTManager.ChangedAdapterSetListener myChangedAdapterSetListener =
647 @Override
648 public void adapterAdded(final BTAdapter adapter) {
649 if( null == chosenAdapter ) {
650 if( initAdapter( adapter ) ) {
651 chosenAdapter = adapter;
652 PrintUtil.println(System.err, "****** Adapter ADDED__: InitOK: " + adapter);
653 } else {
654 PrintUtil.println(System.err, "****** Adapter ADDED__: Ignored: " + adapter);
655 }
656 } else {
657 PrintUtil.println(System.err, "****** Adapter ADDED__: Ignored (other): " + adapter);
658 }
659 }
660
661 @Override
662 public void adapterRemoved(final BTAdapter adapter) {
663 if( null != chosenAdapter && adapter == chosenAdapter ) {
664 chosenAdapter = null;
665 PrintUtil.println(System.err, "****** Adapter REMOVED: " + adapter);
666 } else {
667 PrintUtil.println(System.err, "****** Adapter REMOVED (other): " + adapter);
668 }
669 }
670 };
671
672 public void runTest(final BTManager manager) {
673 timestamp_t0 = Clock.currentTimeMillis();
674
675 boolean done = false;
676
677 manager.addChangedAdapterSetListener(myChangedAdapterSetListener);
678
679 while( !done ) {
680 if( 0 == MULTI_MEASUREMENTS.get() ||
681 ( -1 == MULTI_MEASUREMENTS.get() && !BTDeviceRegistry.isWaitingForAnyDevice() && BTDeviceRegistry.areAllDevicesProcessed() )
682 )
683 {
684 PrintUtil.println(System.err, "****** EOL Test MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS.get()+
686 PrintUtil.println(System.err, "****** WaitForDevices "+BTDeviceRegistry.getWaitForDevicesString());
687 PrintUtil.println(System.err, "****** DevicesProcessed "+BTDeviceRegistry.getProcessedDevicesString());
688 done = true;
689 } else {
690 try {
691 Thread.sleep(2000);
692 } catch (final InterruptedException e) {
693 e.printStackTrace();
694 }
695 }
696 }
697 chosenAdapter = null;
698
699 //
700 // just a manually controlled pull down to show status, not required
701 //
702 final List<BTAdapter> adapters = manager.getAdapters();
703
704 adapters.forEach(new Consumer<BTAdapter>() {
705 @Override
706 public void accept(final BTAdapter a) {
707 PrintUtil.println(System.err, "****** EOL Adapter's Devices - pre_ close: " + a);
708 } } );
709 {
710 final int count = manager.removeChangedAdapterSetListener(myChangedAdapterSetListener);
711 PrintUtil.println(System.err, "****** EOL Removed ChangedAdapterSetCallback " + count);
712
713 // All implicit via destructor or shutdown hook!
714 manager.shutdown(); /* implies: adapter.close(); */
715 }
716 adapters.forEach(new Consumer<BTAdapter>() {
717 @Override
718 public void accept(final BTAdapter a) {
719 PrintUtil.println(System.err, "****** EOL Adapter's Devices - post close: " + a);
720 } } );
721
722 }
723
724 public static void main(final String[] args) throws InterruptedException {
725 for(int i=0; i< args.length; i++) {
726 final String arg = args[i];
727 if( arg.equals("-debug") ) {
728 System.setProperty("org.tinyb.verbose", "true");
729 System.setProperty("org.tinyb.debug", "true");
730 } else if( arg.equals("-verbose") ) {
731 System.setProperty("org.tinyb.verbose", "true");
732 } else if( arg.equals("-dbt_debug") && args.length > (i+1) ) {
733 System.setProperty("direct_bt.debug", args[++i]);
734 } else if( arg.equals("-dbt_verbose") && args.length > (i+1) ) {
735 System.setProperty("direct_bt.verbose", args[++i]);
736 } else if( arg.equals("-dbt_gatt") && args.length > (i+1) ) {
737 System.setProperty("direct_bt.gatt", args[++i]);
738 } else if( arg.equals("-dbt_l2cap") && args.length > (i+1) ) {
739 System.setProperty("direct_bt.l2cap", args[++i]);
740 } else if( arg.equals("-dbt_hci") && args.length > (i+1) ) {
741 System.setProperty("direct_bt.hci", args[++i]);
742 } else if( arg.equals("-dbt_mgmt") && args.length > (i+1) ) {
743 System.setProperty("direct_bt.mgmt", args[++i]);
744 }
745 }
746 final BTManager manager;
747 try {
748 manager = BTFactory.getDirectBTManager();
749 } catch (BTException | NoSuchMethodException | SecurityException
750 | IllegalAccessException | IllegalArgumentException
751 | InvocationTargetException | ClassNotFoundException e) {
752 System.err.println("Unable to instantiate DirectBT BluetoothManager");
753 e.printStackTrace();
754 System.exit(-1);
755 return;
756 }
757 PrintUtil.println(System.err, "Direct-BT BluetoothManager initialized!");
758 PrintUtil.println(System.err, "Direct-BT Native Version "+BTFactory.getNativeVersion()+" (API "+BTFactory.getNativeAPIVersion()+")");
759 PrintUtil.println(System.err, "Direct-BT Java Version "+BTFactory.getImplVersion()+" (API "+BTFactory.getAPIVersion()+")");
760
761 final DBTScanner10 test = new DBTScanner10();
762
763 boolean waitForEnter=false;
764 {
765 for(int i=0; i< args.length; i++) {
766 final String arg = args[i];
767
768 if( arg.equals("-wait") ) {
769 waitForEnter = true;
770 } else if( arg.equals("-show_update_events") ) {
771 test.SHOW_UPDATE_EVENTS = true;
772 } else if( arg.equals("-quiet") ) {
773 test.QUIET = true;
774 } else if( arg.equals("-shutdown") && args.length > (i+1) ) {
775 test.shutdownTest = Integer.valueOf(args[++i]).intValue();
776 } else if( arg.equals("-discoveryPolicy") && args.length > (i+1) ) {
777 test.discoveryPolicy = DiscoveryPolicy.get((byte) Integer.valueOf(args[++i]).intValue() );
778 } else if( arg.equals("-scanPassive") ) {
779 test.le_scan_active = false;
780 } else if( arg.equals("-btmode") && args.length > (i+1) ) {
781 test.btMode = BTMode.get(args[++i]);
782 } else if( arg.equals("-adapter") && args.length > (i+1) ) {
783 test.useAdapter = new EUI48( args[++i] );
784 } else if( arg.equals("-dev") && args.length > (i+1) ) {
786 } else if( arg.equals("-passkey") && args.length > (i+2) ) {
787 final String addrOrNameSub = args[++i];
788 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getOrCreate(addrOrNameSub);
789 sec.passkey = Integer.valueOf(args[++i]).intValue();
790 System.err.println("Set passkey in "+sec);
791 } else if( arg.equals("-seclevel") && args.length > (i+2) ) {
792 final String addrOrNameSub = args[++i];
793 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getOrCreate(addrOrNameSub);
794 final int sec_level_i = Integer.valueOf(args[++i]).intValue();
795 sec.sec_level = BTSecurityLevel.get( (byte)( sec_level_i & 0xff ) );
796 System.err.println("Set sec_level "+sec_level_i+" in "+sec);
797 } else if( arg.equals("-iocap") && args.length > (i+2) ) {
798 final String addrOrNameSub = args[++i];
799 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getOrCreate(addrOrNameSub);
800 final int io_cap_i = Integer.valueOf(args[++i]).intValue();
801 sec.io_cap = SMPIOCapability.get( (byte)( io_cap_i & 0xff ) );
802 System.err.println("Set io_cap "+io_cap_i+" in "+sec);
803 } else if( arg.equals("-secauto") && args.length > (i+2) ) {
804 final String addrOrNameSub = args[++i];
805 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getOrCreate(addrOrNameSub);
806 final int io_cap_i = Integer.valueOf(args[++i]).intValue();
807 sec.io_cap_auto = SMPIOCapability.get( (byte)( io_cap_i & 0xff ) );
808 System.err.println("Set SEC AUTO security io_cap "+io_cap_i+" in "+sec);
809 } else if( arg.equals("-cmd") && args.length > (i+1) ) {
810 test.cmd_uuid = args[++i];
811 } else if( arg.equals("-cmdrsp") && args.length > (i+1) ) {
812 test.cmd_rsp_uuid = args[++i];
813 } else if( arg.equals("-cmdarg") && args.length > (i+1) ) {
814 test.cmd_arg = (byte)Integer.valueOf(args[++i]).intValue();
815 } else if( arg.equals("-disconnect") ) {
816 test.KEEP_CONNECTED = false;
817 } else if( arg.equals("-enableGATTPing") ) {
818 test.GATT_PING_ENABLED = true;
819 } else if( arg.equals("-keepDevice") ) {
820 test.REMOVE_DEVICE = false;
821 } else if( arg.equals("-count") && args.length > (i+1) ) {
822 test.MULTI_MEASUREMENTS.set(Integer.valueOf(args[++i]).intValue());
823 } else if( arg.equals("-single") ) {
824 test.MULTI_MEASUREMENTS.set(-1);
825 } else if( arg.equals("-resetEachCon") && args.length > (i+1) ) {
826 test.RESET_ADAPTER_EACH_CONN = Integer.valueOf(args[++i]).intValue();
827 }
828 }
829 PrintUtil.println(System.err, "Run with '[-btmode LE|BREDR|DUAL] "+
830 "[-disconnect] [-enableGATTPing] [-count <number>] [-single] [-show_update_events] [-quiet] "+
831 "[-discoveryPolicy <0-4>] "+
832 "[-scanPassive] "+
833 "[-resetEachCon connectionCount] "+
834 "[-adapter <adapter_address>] "+
835 "(-dev <device_[address|name]_sub>)* "+
836 "(-seclevel <device_[address|name]_sub> <int_sec_level>)* "+
837 "(-iocap <device_[address|name]_sub> <int_iocap>)* "+
838 "(-secauto <device_[address|name]_sub> <int_iocap>)* "+
839 "(-passkey <device_[address|name]_sub> <digits>)* "+
840 "[-cmd <uuid>] [-cmdrsp <uuid>] [-cmdarg <byte-val>] "+
841 "[-verbose] [-debug] "+
842 "[-dbt_verbose true|false] "+
843 "[-dbt_debug true|false|adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event] "+
844 "[-dbt_mgmt cmd.timeout=3000,ringsize=64,...] "+
845 "[-dbt_hci cmd.complete.timeout=10000,cmd.status.timeout=3000,ringsize=64,...] "+
846 "[-dbt_gatt cmd.read.timeout=500,cmd.write.timeout=500,cmd.init.timeout=2500,ringsize=128,...] "+
847 "[-dbt_l2cap reader.timeout=10000,restart.count=0,...] "+
848 "[-shutdown <int>]'");
849 }
850
851 PrintUtil.println(System.err, "MULTI_MEASUREMENTS "+test.MULTI_MEASUREMENTS.get());
852 PrintUtil.println(System.err, "KEEP_CONNECTED "+test.KEEP_CONNECTED);
853 PrintUtil.println(System.err, "RESET_ADAPTER_EACH_CONN "+test.RESET_ADAPTER_EACH_CONN);
854 PrintUtil.println(System.err, "GATT_PING_ENABLED "+test.GATT_PING_ENABLED);
855 PrintUtil.println(System.err, "REMOVE_DEVICE "+test.REMOVE_DEVICE);
856 PrintUtil.println(System.err, "SHOW_UPDATE_EVENTS "+test.SHOW_UPDATE_EVENTS);
857 PrintUtil.println(System.err, "QUIET "+test.QUIET);
858 PrintUtil.println(System.err, "adapter "+test.useAdapter);
859 PrintUtil.println(System.err, "btmode "+test.btMode.toString());
860 PrintUtil.println(System.err, "discoveryPolicy "+test.discoveryPolicy.toString());
861 PrintUtil.println(System.err, "le_scan_active "+test.le_scan_active);
862 PrintUtil.println(System.err, "Command: cmd "+test.cmd_uuid+", arg 0x"+Integer.toHexString(test.cmd_arg));
863 PrintUtil.println(System.err, " rsp "+test.cmd_rsp_uuid);
864 PrintUtil.println(System.err, "security-details: "+BTSecurityRegistry.allToString() );
865 PrintUtil.println(System.err, "waitForDevices: "+BTDeviceRegistry.getWaitForDevicesString());
866
867 if( waitForEnter ) {
868 PrintUtil.println(System.err, "Press ENTER to continue\n");
869 try{ System.in.read();
870 } catch(final Exception e) { }
871 }
872 test.runTest(manager);
873 }
874}
Author: Sven Gothel sgothel@jausoft.com Copyright (c) 2021 Gothel Software e.K.
static final String CLIENT_KEY_PATH
This Java scanner BTRole::Master GATT client example uses an event driven workflow and multithreading...
static void main(final String[] args)
void runTest(final BTManager manager)
final void shutdown()
Release the native memory associated with this object and all related Bluetooth resources.
static final BTManager getManager()
Returns an instance of BluetoothManager, to be used instead of constructor.
Author: Sven Gothel sgothel@jausoft.com Copyright (c) 2020 Gothel Software e.K.
boolean isSet(final SettingType bit)
BTAdapter status listener for remote BTDevice discovery events: Added, updated and removed; as well a...
Application toolkit providing BT device registration of processed and awaited devices.
static void addToProcessedDevices(final BDAddressAndType a, final String n)
static boolean isDeviceProcessed(final BDAddressAndType a)
static boolean isWaitingForDevice(final EUI48 address, final String name, final DeviceQueryMatch m)
Returns true if the given address and/or name matches any of the awaited devices.
static boolean areAllDevicesProcessed(final DeviceQueryMatch m)
Returns true if all awaited devices have been processed.
static void addToWaitForDevices(final String addrOrNameSub)
One stop BTManager API entry point.
Definition: BTFactory.java:52
static native String getNativeVersion()
static final String getImplVersion()
Manifest's Attributes.Name#IMPLEMENTATION_VERSION or null if not available.
Definition: BTFactory.java:130
static final String getAPIVersion()
Manifest's Attributes.Name#SPECIFICATION_VERSION or null if not available.
Definition: BTFactory.java:124
static native String getNativeAPIVersion()
static synchronized BTManager getDirectBTManager()
Returns an initialized BluetoothManager instance using the DirectBT implementation.
Definition: BTFactory.java:472
BTGattChar event listener for notification and indication events.
Class maps a GATT command and optionally its asynchronous response to a synchronous atomic operation.
Definition: BTGattCmd.java:49
void setVerbose(final boolean v)
Set verbosity for UUID resolution.
Definition: BTGattCmd.java:318
synchronized HCIStatusCode send(final boolean prefNoAck, final byte[] cmd_data, final int timeoutMS)
Send the command to the remote BTDevice.
Definition: BTGattCmd.java:361
void close()
Close this command instance, usually called at destruction.
Definition: BTGattCmd.java:202
byte[] getResponse()
Returns the read-only response data object for configured commands with response notification or indi...
Definition: BTGattCmd.java:327
synchronized boolean isResolved()
Query whether all UUIDs of this commands have been resolved.
Definition: BTGattCmd.java:341
boolean hasResponseSet()
Return true if a notification or indication response has been set via constructor,...
Definition: BTGattCmd.java:312
Application toolkit providing BT security setup and its device association on a pattern matching basi...
static BTSecurityRegistry.Entry getStartOf(final EUI48 addr, final String name)
Returns a matching BTSecurityRegistry.Entry,.
static BTSecurityRegistry.Entry getOrCreate(final String addrOrNameSub)
Determines whether the given addrOrNameSub is a EUI48Sub or just a name and retrieves an entry.
static String decodeUTF8String(final byte[] buffer, final int offset, final int size)
Decodes the given consecutive UTF-8 characters within buffer to String.
Definition: BTUtils.java:91
static short getHCIConnSupervisorTimeout(final int conn_latency, final int conn_interval_max_ms, final int min_result_ms, final int multiplier)
Defining the supervising timeout for LE connections to be a multiple of the maximum connection interv...
Definition: BTUtils.java:62
Bit mask of 'Extended Inquiry Response' (EIR) data fields, indicating a set of related data.
Collection of 'Extended Advertising Data' (EAD), 'Advertising Data' (AD) or 'Extended Inquiry Respons...
final boolean isSet(final EIRDataTypeSet.DataType bit)
native String toString(final boolean includeServices)
native void getConnInterval(final short minmax[])
Get slave connection interval range.
Bit mask of GATT Characteristic Properties.
LE Link Layer Feature Set (bitmask)
LE Transport PHY bit values (bitmask)
Definition: LE_PHYs.java:36
Storage for SMP keys including required connection parameter per local adapter and remote device.
Definition: SMPKeyBin.java:75
final static boolean remove(final String path, final BTDevice remoteDevice_)
Definition: SMPKeyBin.java:555
final static String getFilename(final String path, final BDAddressAndType localAddress_, final BDAddressAndType remoteAddress_)
Definition: SMPKeyBin.java:459
static boolean createAndWrite(final BTDevice device, final String path, final boolean verbose_)
Create a new SMPKeyBin instance on the fly based upon given BTDevice's BTSecurityLevel,...
Definition: SMPKeyBin.java:229
Bits representing 'BTAdapter setting' data fields.
Bluetooth adapter operating mode.
Definition: BTMode.java:34
DUAL
Dual Bluetooth mode, i.e.
Definition: BTMode.java:38
static BTMode get(final String name)
Maps the specified name to a constant of BTMode.
Definition: BTMode.java:57
Bluetooth Security Level.
UNSET
Security Level not set, value 0.
static BTSecurityLevel get(final String name)
Maps the specified name to a constant of BTSecurityLevel.
Discovery policy defines the BTAdapter discovery mode after connecting a remote BTDevice:
PAUSE_CONNECTED_UNTIL_DISCONNECTED
Pause discovery until all connected BTDevice become disconnected, effectively until AdapterStatusList...
static DiscoveryPolicy get(final byte value)
Maps the specified integer value to a constant of DiscoveryPolicy.
PAUSE_CONNECTED_UNTIL_READY
Pause discovery until all connected BTDevice reach readiness inclusive optional SMP pairing (~120ms) ...
Each enum represents a 'Extended Inquiry Response' (EIR) data field type bit.
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1.1 Characteristic Properties.
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
Each enum represents a 'LE Transport PHY' bit value.
Definition: LE_PHYs.java:43
Bluetooth secure pairing mode.
SMP IO Capability value.
static SMPIOCapability get(final String name)
Maps the specified name to a constant of SMPIOCapability.
KEYBOARD_ONLY
Keyboard input only, value 2.
SMP Pairing Process state definition.
Meta ScanType as derived from BTMode with defined value mask consisting of BDAddressType bits.
Definition: ScanType.java:36
BTAdapter represents one local Bluetooth Controller.
Definition: BTAdapter.java:48
HCIStatusCode startDiscovery()
Starts discovery using all default arguments, see startDiscovery(DiscoveryPolicy, boolean,...
boolean isInitialized()
Returns true, if initialize(BTMode) has already been called for this adapter, otherwise false.
HCIStatusCode reset()
Reset the adapter.
LE_Features getLEFeatures()
Return LE_Features for this controller.
boolean setPowered(final boolean power_on)
Sets the power state the adapter.
boolean addStatusListener(final AdapterStatusListener listener)
Add the given AdapterStatusListener to the list if not already present.
HCIStatusCode setDefaultLE_PHY(final LE_PHYs Tx, final LE_PHYs Rx)
Sets default preference of LE_PHYs.
BDAddressAndType getAddressAndType()
Returns the adapter's public BDAddressAndType.
boolean removeDevicePausingDiscovery(final BTDevice device)
Manual DiscoveryPolicy intervention point, allowing user to remove the ready device from the queue of...
boolean removeStatusListener(final AdapterStatusListener l)
Remove the given AdapterStatusListener from the list.
int getBTMajorVersion()
Returns the Bluetooth major version of this adapter.
HCIStatusCode initialize(final BTMode btMode, boolean powerOn)
Initialize the adapter with default values, including power-on.
BTDevice represents one remote Bluetooth device.
Definition: BTDevice.java:47
HCIStatusCode setPairingNumericComparison(final boolean equal)
Method sets the numeric comparison result, see PairingMode#NUMERIC_COMPARE_ini.
BTAdapter getAdapter()
Returns the adapter on which this device was discovered or connected.
String getName()
Returns the remote device name.
EInfoReport getEIRInd()
Return the latest advertised EInfoReport AD_IND variant for this remote device.
HCIStatusCode uploadKeys()
Upload all set keys to the adapter for pre-pairing.
EInfoReport getEIRScanRsp()
Return the latest advertised EInfoReport AD_SCAN_RSP for this remote device.
boolean setConnSecurityAuto(final SMPIOCapability iocap_auto)
Set automatic security negotiation of BTSecurityLevel and SMPIOCapability pairing mode.
boolean setConnSecurity(final BTSecurityLevel sec_level, final SMPIOCapability io_cap)
Sets the given BTSecurityLevel and SMPIOCapability used to connect to this device on the upcoming con...
boolean pingGATT()
Issues a GATT ping to the device, validating whether it is still reachable.
boolean remove()
Remove this device from the system (like an unpair).
List< BTGattService > getGattServices()
Returns a complete list of shared BTGattService available on this device, initially retrieved via GAT...
HCIStatusCode getConnectedLE_PHY(LE_PHYs[] resRx, LE_PHYs[] resTx)
Request and return LE_PHYs bit for the given connection.
int removeAllCharListener()
Remove all BTGattCharListener from the list.
EInfoReport getEIR()
Return the merged advertised EInfoReport for this remote device.
long getLastDiscoveryTimestamp()
Returns the timestamp in monotonic milliseconds when this device instance has discovered or connected...
HCIStatusCode connectLE(final short le_scan_interval, final short le_scan_window, final short conn_interval_min, final short conn_interval_max, final short conn_latency, final short supervision_timeout)
Establish a HCI BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM connection to this device.
boolean addStatusListener(final AdapterStatusListener listener)
Add the given AdapterStatusListener to the list if not already present, listening only for events mat...
HCIStatusCode setPairingPasskey(final int passkey)
Method sets the given passkey entry, see PairingMode#PASSKEY_ENTRY_ini.
BDAddressAndType getAddressAndType()
Returns the devices' unique EUI48 address and BDAddressType type tuple, might be its initially report...
HCIStatusCode setConnectedLE_PHY(final LE_PHYs Tx, final LE_PHYs Rx)
Sets preference of used LE_PHYs for the given connection.
Representing a Gatt Characteristic object from the GATT client perspective.
Definition: BTGattChar.java:49
String getUUID()
Get the UUID of this characteristic.
List< BTGattDesc > getDescriptors()
Returns a list of BluetoothGattDescriptors this characteristic exposes.
boolean enableNotificationOrIndication(final boolean enabledState[])
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration.
boolean addCharListener(final BTGattCharListener listener)
Add the given BTGattCharListener to the listener list if not already present.
GattCharPropertySet getProperties()
Returns the properties of this characteristic.
byte[] readValue()
Reads the value of this characteristic.
Representing a Gatt Characteristic Descriptor object from the GATT client perspective.
Definition: BTGattDesc.java:42
String getUUID()
Get the UUID of this descriptor.
Representing a Gatt Service object from the GATT client perspective.
String getUUID()
Get the UUID of this service.
List< BTGattChar > getChars()
Returns a list of BTGattChar this service exposes.
Event listener to receive change events regarding the system's BTAdapter set, e.g.
Definition: BTManager.java:53
A thread safe singleton handler of the BTAdapter manager, e.g.
Definition: BTManager.java:36
void addChangedAdapterSetListener(final ChangedAdapterSetListener l)
Add the given ChangedAdapterSetListener to this manager.
List< BTAdapter > getAdapters()
Returns a list of BluetoothAdapters available in the system.
int removeChangedAdapterSetListener(final ChangedAdapterSetListener l)
Remove the given ChangedAdapterSetListener from this manager.
void shutdown()
Release the native memory associated with this object and all related Bluetooth resources.