Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
DBTLabPadClient01.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.nio.charset.StandardCharsets;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Iterator;
31import java.util.List;
32import java.util.concurrent.atomic.AtomicInteger;
33import java.util.function.Consumer;
34
35import org.direct_bt.AdapterSettings;
36import org.direct_bt.AdapterStatusListener;
37import org.direct_bt.BTMode;
38import org.direct_bt.BTSecurityLevel;
39import org.direct_bt.BTAdapter;
40import org.direct_bt.BTDevice;
41import org.direct_bt.BTDeviceRegistry;
42import org.direct_bt.BTException;
43import org.direct_bt.BTFactory;
44import org.direct_bt.BTGattChar;
45import org.direct_bt.BTGattCharListener;
46import org.direct_bt.BTGattCmd;
47import org.direct_bt.BTGattDesc;
48import org.direct_bt.BTGattService;
49import org.direct_bt.BTManager;
50import org.direct_bt.BTSecurityRegistry;
51import org.direct_bt.BTUtils;
52import org.direct_bt.DBGattChar;
53import org.direct_bt.DBGattDesc;
54import org.direct_bt.DBGattServer;
55import org.direct_bt.DBGattService;
56import org.direct_bt.DBGattValue;
57import org.direct_bt.DiscoveryPolicy;
58import org.direct_bt.EIRDataTypeSet;
59import org.direct_bt.EInfoReport;
60import org.direct_bt.GattCharPropertySet;
61import org.direct_bt.HCIStatusCode;
62import org.direct_bt.LE_Features;
63import org.direct_bt.LE_PHYs;
64import org.direct_bt.PairingMode;
65import org.direct_bt.SMPIOCapability;
66import org.direct_bt.SMPKeyBin;
67import org.direct_bt.SMPPairingState;
68import org.direct_bt.ScanType;
69import org.jau.io.PrintUtil;
70import org.jau.net.EUI48;
71import org.jau.sys.Clock;
72import org.jau.util.BasicTypes;
73
74import jau.direct_bt.DBTManager;
75
76/**
77 * This Java example demonstrates a client connecting to `Avalun's LabPad device`.
78 *
79 * It differs from _dbt_scanner10_ as follows:
80 *
81 * * Employs a minimal GattServer supplying `Generic Access` service
82 *
83 * * Performing one simple Gatt write and indication listener test
84 *
85 * * Uses pre-set `-dev LabPad` device name and SMPIOCapability::KEYBOARD_ONLY + BTSecurityLevel::ENC_AUTH
86 *
87 * * Commandline `-passkey <int>` uses `LabPad` implicitly, i.e. user only needs to pass the integer w/o device name.
88 *
89 * Other than that, please refer to _dbt_scanner10_ as a general example.
90 */
91public class DBTLabPadClient01 {
92 long timestamp_t0;
93
94 EUI48 useAdapter = EUI48.ALL_DEVICE;
95 BTMode btMode = BTMode.DUAL;
96 BTAdapter chosenAdapter = null;
97
98 int RESET_ADAPTER_EACH_CONN = 0;
99 AtomicInteger deviceReadyCount = new AtomicInteger(0);
100
101 AtomicInteger MULTI_MEASUREMENTS = new AtomicInteger(8);
102 boolean KEEP_CONNECTED = true;
103
104 boolean GATT_PING_ENABLED = false;
105 boolean REMOVE_DEVICE = true;
106
107 // Avalun's LabPad Comman + Event UUID
108 static String cmd_req_uuid = "2c1b2472-4a5f-11e5-9595-0002a5d5c51b";
109 static String cmd_rsp_uuid = "2c1b2473-4a5f-11e5-9595-0002a5d5c51b";
110
111 static final byte[] cmd_data1 = { 0x00 /* cmd-idx-0 */,
112 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
113 0x01, 0x5E, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
114 (byte)0x9B, (byte)0x23, (byte)0x84 };
115
116 static final byte[] cmd_data2 = { 0x01 /* cmd-idx-1 */,
117 (byte)0xB8 };
118
119 static final byte[] resp_exp = { 0x00 /* rsp-idx-0 */,
120 0x14, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00,
121 0x01, (byte)0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 (byte)0xf6, 0x64, 0x17,
123 0x01 /* rsp-idx-1 */, (byte)0xed };
124
125 boolean SHOW_UPDATE_EVENTS = false;
126 boolean QUIET = false;
127
128 int shutdownTest = 0;
129
130 static void executeOffThread(final Runnable runobj, final String threadName, final boolean detach) {
131 final Thread t = new Thread( runobj, threadName );
132 if( detach ) {
133 t.setDaemon(true); // detach thread
134 }
135 t.start();
136 }
137 static void execute(final Runnable task, final boolean offThread) {
138 if( offThread ) {
139 final Thread t = new Thread(task);
140 t.setDaemon(true);
141 t.start();
142 } else {
143 task.run();
144 }
145 }
146
147 private final DBGattServer dbGattServer = new DBGattServer(
148 /* services: */
149 Arrays.asList( // DBGattService
150 new DBGattService ( true /* primary */,
152 Arrays.asList( // DBGattChar
153 new DBGattChar( DBGattChar.UUID16.DEVICE_NAME /* value_type_ */,
155 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
156 DBGattValue.make("Jausoft_Dev", 128) /* value */ ),
157 new DBGattChar( DBGattChar.UUID16.APPEARANCE /* value_type_ */,
159 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
160 DBGattValue.make((short)0) /* value */ )
161 ) ),
162 new DBGattService ( true /* primary */,
164 Arrays.asList( // DBGattChar
167 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
168 DBGattValue.make("Gothel Software") /* value */ ),
169 new DBGattChar( DBGattChar.UUID16.MODEL_NUMBER_STRING /* value_type_ */,
171 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
172 DBGattValue.make("2.4.0-pre") /* value */ ),
173 new DBGattChar( DBGattChar.UUID16.SERIAL_NUMBER_STRING /* value_type_ */,
175 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
176 DBGattValue.make("sn:0123456789") /* value */ ),
179 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
180 DBGattValue.make("hw:0123456789") /* value */ ),
183 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
184 DBGattValue.make("fw:0123456789") /* value */ ),
187 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
188 DBGattValue.make("sw:0123456789") /* value */ )
189 ) )
190 ) );
191
192 class MyAdapterStatusListener extends AdapterStatusListener {
193 @Override
194 public void adapterSettingsChanged(final BTAdapter adapter, final AdapterSettings oldmask,
195 final AdapterSettings newmask, final AdapterSettings changedmask, final long timestamp) {
196 final boolean initialSetting = oldmask.isEmpty();
197 if( initialSetting ) {
198 PrintUtil.println(System.err, "****** SETTINGS: "+oldmask+" -> "+newmask+", initial "+changedmask);
199 } else {
200 PrintUtil.println(System.err, "****** SETTINGS: "+oldmask+" -> "+newmask+", changed "+changedmask);
201 }
202 PrintUtil.println(System.err, "Status Adapter:");
203 PrintUtil.println(System.err, adapter.toString());
204 if( !initialSetting &&
207 {
208
209 executeOffThread( () -> { startDiscovery(adapter, "powered-on"); },
210 "DBT-StartDiscovery-"+adapter.getAddressAndType(), true /* detach */);
211 }
212 }
213
214 @Override
215 public void discoveringChanged(final BTAdapter adapter, final ScanType currentMeta, final ScanType changedType, final boolean changedEnabled, final DiscoveryPolicy policy, final long timestamp) {
216 PrintUtil.println(System.err, "****** DISCOVERING: meta "+currentMeta+", changed["+changedType+", enabled "+changedEnabled+", policy "+policy+"] on "+adapter);
217 }
218
219 @Override
220 public boolean deviceFound(final BTDevice device, final long timestamp) {
223 ( 0 < MULTI_MEASUREMENTS.get() || !BTDeviceRegistry.isDeviceProcessed(device.getAddressAndType()) )
224 )
225 )
226 {
227 PrintUtil.println(System.err, "****** FOUND__-0: Connecting "+device.toString());
228 {
229 final long td = Clock.currentTimeMillis() - timestamp_t0; // adapter-init -> now
230 PrintUtil.println(System.err, "PERF: adapter-init -> FOUND__-0 " + td + " ms");
231 }
232 executeOffThread( () -> { connectDiscoveredDevice(device); },
233 "DBT-Connect-"+device.getAddressAndType(), true /* detach */);
234 return true;
235 } else {
236 if( !QUIET ) {
237 PrintUtil.println(System.err, "****** FOUND__-1: NOP "+device.toString());
238 }
239 return false;
240 }
241 }
242
243 @Override
244 public void deviceUpdated(final BTDevice device, final EIRDataTypeSet updateMask, final long timestamp) {
245 if( !QUIET && SHOW_UPDATE_EVENTS ) {
246 PrintUtil.println(System.err, "****** UPDATED: "+updateMask+" of "+device);
247 }
248 }
249
250 @Override
251 public void deviceConnected(final BTDevice device, final boolean discovered, final long timestamp) {
252 PrintUtil.println(System.err, "****** CONNECTED (discovered "+discovered+"): "+device.toString());
253 }
254
255 @Override
256 public void devicePairingState(final BTDevice device, final SMPPairingState state, final PairingMode mode, final long timestamp) {
257 PrintUtil.println(System.err, "****** PAIRING_STATE: state "+state+", mode "+mode+": "+device);
258 switch( state ) {
259 case NONE:
260 // next: deviceReady(..)
261 break;
262 case FAILED: {
263 final boolean res = SMPKeyBin.remove(DBTConstants.CLIENT_KEY_PATH, device);
264 PrintUtil.println(System.err, "****** PAIRING_STATE: state "+state+"; Remove key file "+SMPKeyBin.getFilename(DBTConstants.CLIENT_KEY_PATH, device)+", res "+res);
265 // next: deviceReady() or deviceDisconnected(..)
266 } break;
267 case REQUESTED_BY_RESPONDER:
268 // next: FEATURE_EXCHANGE_STARTED
269 break;
270 case FEATURE_EXCHANGE_STARTED:
271 // next: FEATURE_EXCHANGE_COMPLETED
272 break;
273 case FEATURE_EXCHANGE_COMPLETED:
274 // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION
275 break;
276 case PASSKEY_EXPECTED: {
277 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, device.getName());
278 if( null != sec && sec.getPairingPasskey() != BTSecurityRegistry.NO_PASSKEY ) {
279 executeOffThread( () -> { device.setPairingPasskey( sec.getPairingPasskey() ); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */);
280 } else {
281 executeOffThread( () -> { device.setPairingPasskey( 0 ); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */);
282 // 3s disconnect: executeOffThread( () -> { device.setPairingPasskeyNegative(); }, "DBT-SetPasskeyNegative-"+device.getAddressAndType(), true /* detach */);
283 }
284 // next: KEY_DISTRIBUTION or FAILED
285 } break;
286 case NUMERIC_COMPARE_EXPECTED: {
287 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, device.getName());
288 if( null != sec ) {
289 executeOffThread( () -> { device.setPairingNumericComparison( sec.getPairingNumericComparison() ); }, "DBT-SetNumericComp-"+device.getAddressAndType(), true /* detach */);
290 } else {
291 executeOffThread( () -> { device.setPairingNumericComparison( false ); }, "DBT-SetNumericCompFalse-"+device.getAddressAndType(), true /* detach */);
292 }
293 // next: KEY_DISTRIBUTION or FAILED
294 } break;
295 case OOB_EXPECTED:
296 // FIXME: ABORT
297 break;
298 case KEY_DISTRIBUTION:
299 // next: COMPLETED or FAILED
300 break;
301 case COMPLETED:
302 // next: deviceReady(..)
303 break;
304 default: // nop
305 break;
306 }
307 }
308
309 @Override
310 public void deviceReady(final BTDevice device, final long timestamp) {
311 deviceReadyCount.incrementAndGet();
312 PrintUtil.println(System.err, "****** READY-0: Processing["+deviceReadyCount.get()+"] "+device.toString());
313 {
314 final long td = Clock.currentTimeMillis() - timestamp_t0; // adapter-init -> now
315 PrintUtil.println(System.err, "PERF: adapter-init -> READY-0 " + td + " ms");
316 }
317 processReadyDevice(device); // AdapterStatusListener::deviceReady() explicitly allows prolonged and complex code execution!
318 }
319
320 @Override
321 public void deviceDisconnected(final BTDevice device, final HCIStatusCode reason, final short handle, final long timestamp) {
322 PrintUtil.println(System.err, "****** DISCONNECTED: Reason "+reason+", old handle 0x"+Integer.toHexString(handle)+": "+device+" on "+device.getAdapter());
323
324 if( REMOVE_DEVICE ) {
325 executeOffThread( () -> { removeDevice(device); }, "DBT-Remove-"+device.getAddressAndType(), true /* detach */);
326 }
327 if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount.get() % RESET_ADAPTER_EACH_CONN ) {
328 executeOffThread( () -> { resetAdapter(device.getAdapter(), 1); },
329 "DBT-Reset-"+device.getAdapter().getAddressAndType(), true /* detach */ );
330 }
331 }
332
333 @Override
334 public String toString() {
335 return "AdapterStatusListener[user, per-adapter]";
336 }
337 };
338
339 class MyGATTEventListener extends BTGattCharListener {
340 private final int i, j;
341
342 public MyGATTEventListener(final int i_, final int j_) { i=i_; j=j_; }
343
344 @Override
345 public void notificationReceived(final BTGattChar charDecl,
346 final byte[] value, final long timestamp) {
347 final long tR = Clock.currentTimeMillis();
348 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Characteristic-Notify: UUID %s, td %d ******\n",
349 i, j, charDecl.getUUID(), (tR-timestamp));
350 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString());
351 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BasicTypes.bytesHexString(value, 0, -1, true));
352 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Value S: %s ******\n", i, j, BTUtils.decodeUTF8String(value, 0, value.length));
353
354 shutdownTest();
355 }
356
357 @Override
358 public void indicationReceived(final BTGattChar charDecl,
359 final byte[] value, final long timestamp, final boolean confirmationSent) {
360 final long tR = Clock.currentTimeMillis();
361 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Characteristic-Indication: UUID %s, td %d, confirmed %b ******\n",
362 i, j, charDecl.getUUID(), (tR-timestamp), confirmationSent);
363 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString());
364 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BasicTypes.bytesHexString(value, 0, -1, true));
365 PrintUtil.fprintf_td(System.err, "**[%02d.%02d] Value S: %s ******\n", i, j, BTUtils.decodeUTF8String(value, 0, value.length));
366
367 shutdownTest();
368 }
369 }
370
371 private void connectDiscoveredDevice(final BTDevice device) {
372 PrintUtil.println(System.err, "****** Connecting Device: Start " + device.toString());
373
374 // Testing listener lifecycle @ device dtor
376 @Override
377 public void deviceUpdated(final BTDevice device, final EIRDataTypeSet updateMask, final long timestamp) {
378 if( SHOW_UPDATE_EVENTS ) {
379 PrintUtil.println(System.err, "****** UPDATED(2): "+updateMask+" of "+device);
380 }
381 }
382 @Override
383 public void deviceConnected(final BTDevice device, final boolean discovered, final long timestamp) {
384 PrintUtil.println(System.err, "****** CONNECTED(2) (discovered "+discovered+"): "+device.toString());
385 }
386
387 @Override
388 public String toString() {
389 return "TempAdapterStatusListener[user, device "+device.getAddressAndType().toString()+"]";
390 } });
391
392 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, device.getName());
393 if( null != sec ) {
394 PrintUtil.println(System.err, "****** Connecting Device: Found SecurityDetail "+sec.toString()+" for "+device.toString());
395 } else {
396 PrintUtil.println(System.err, "****** Connecting Device: No SecurityDetail for "+device.toString());
397 }
398 final BTSecurityLevel req_sec_level = null != sec ? sec.getSecLevel() : BTSecurityLevel.UNSET;
399 HCIStatusCode res = device.uploadKeys(DBTConstants.CLIENT_KEY_PATH, req_sec_level, true /* verbose_ */);
400 PrintUtil.fprintf_td(System.err, "****** Connecting Device: BTDevice::uploadKeys(...) result %s\n", res.toString());
401 if( HCIStatusCode.SUCCESS != res ) {
402 if( null != sec ) {
403 if( sec.isSecurityAutoEnabled() ) {
404 final boolean r = device.setConnSecurityAuto( sec.getSecurityAutoIOCap() );
405 PrintUtil.println(System.err, "****** Connecting Device: Using SecurityDetail.SEC AUTO "+sec+" -> set OK "+r);
406 } else if( sec.isSecLevelOrIOCapSet() ) {
407 final boolean r = device.setConnSecurity(sec.getSecLevel(), sec.getIOCap());
408 PrintUtil.println(System.err, "****** Connecting Device: Using SecurityDetail.Level+IOCap "+sec+" -> set OK "+r);
409 } else {
410 final boolean r = device.setConnSecurityAuto( SMPIOCapability.KEYBOARD_ONLY );
411 PrintUtil.println(System.err, "****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY ("+sec+") -> set OK "+r);
412 }
413 } else {
414 final boolean r = device.setConnSecurityAuto( SMPIOCapability.KEYBOARD_ONLY );
415 PrintUtil.println(System.err, "****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY -> set OK "+r);
416 }
417 }
418 final EInfoReport eir = device.getEIR();
419 PrintUtil.println(System.err, "EIR-1 "+device.getEIRInd().toString());
420 PrintUtil.println(System.err, "EIR-2 "+device.getEIRScanRsp().toString());
421 PrintUtil.println(System.err, "EIR-+ "+eir.toString());
422
423 short conn_interval_min = (short)8; // 10ms
424 short conn_interval_max = (short)12; // 15ms
425 final short conn_latency = (short)0;
427 final short[] minmax = new short[2];
428 eir.getConnInterval(minmax);
429 conn_interval_min = minmax[0];
430 conn_interval_max = minmax[1];
431 }
432 final short supervision_timeout = BTUtils.getHCIConnSupervisorTimeout(conn_latency, (int) ( conn_interval_max * 1.25 ) /* ms */);
433 res = device.connectLE(le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max, conn_latency, supervision_timeout);
434 PrintUtil.println(System.err, "****** Connecting Device Command, res "+res+": End result "+res+" of " + device.toString());
435 }
436
437 void shutdownTest() {
438 switch( shutdownTest ) {
439 case 1:
440 shutdownTest01();
441 break;
442 case 2:
443 shutdownTest02();
444 break;
445 default:
446 // nop
447 }
448 }
449 void shutdownTest01() {
450 execute( () -> {
452 mngr.shutdown();
453 }, true);
454 }
455 void shutdownTest02() {
456 execute( () -> {
457 System.exit(1);
458 }, true);
459 }
460
461 private void processReadyDevice(final BTDevice device) {
462 PrintUtil.println(System.err, "****** Processing Ready Device: Start " + device.toString());
463 final long t1 = Clock.currentTimeMillis();
464
465 SMPKeyBin.createAndWrite(device, DBTConstants.CLIENT_KEY_PATH, true /* verbose */);
466 final long t2 = Clock.currentTimeMillis();
467
468 boolean success = false;
469
470 if( device.getAdapter().getBTMajorVersion() > 4 ) {
471 final LE_PHYs Tx = new LE_PHYs(LE_PHYs.PHY.LE_2M);
472 final LE_PHYs Rx = new LE_PHYs(LE_PHYs.PHY.LE_2M);
473
474 final HCIStatusCode res = device.setConnectedLE_PHY(Tx, Rx);
475 PrintUtil.fprintf_td(System.err, "****** Set Connected LE PHY: status %s: Tx %s, Rx %s\n",
476 res.toString(), Tx.toString(), Rx.toString());
477 }
478 {
479 final LE_PHYs resTx[] = { new LE_PHYs() };
480 final LE_PHYs resRx[] = { new LE_PHYs() };
481 final HCIStatusCode res = device.getConnectedLE_PHY(resTx, resRx);
482 PrintUtil.fprintf_td(System.err, "****** Got Connected LE PHY: status %s: Tx %s, Rx %s\n",
483 res.toString(), resTx[0].toString(), resRx[0].toString());
484 }
485 final long t3 = Clock.currentTimeMillis();
486
487 //
488 // GATT Service Processing
489 //
490 try {
491 final List<BTGattService> primServices = device.getGattServices();
492 if( null == primServices || 0 == primServices.size() ) {
493 // Cheating the flow, but avoiding: goto, do-while-false and lastly unreadable intendations
494 // And it is an error case nonetheless ;-)
495 throw new RuntimeException("Processing Ready Device: getServices() failed " + device.toString());
496 }
497 final long t5 = Clock.currentTimeMillis();
498 {
499 final long td00 = device.getLastDiscoveryTimestamp() - timestamp_t0; // adapter-init to discovered
500 final long td01 = t1 - timestamp_t0; // adapter-init to processing-start
501 final long td05 = t5 - timestamp_t0; // adapter-init -> gatt-complete
502 final long tdc1 = t1 - device.getLastDiscoveryTimestamp(); // discovered to processing-start
503 final long tdc5 = t5 - device.getLastDiscoveryTimestamp(); // discovered to gatt-complete
504 final long td12 = t2 - t1; // SMPKeyBin
505 final long td23 = t3 - t2; // LE_PHY
506 final long td13 = t3 - t1; // SMPKeyBin + LE_PHY
507 final long td35 = t5 - t3; // get-gatt-services
508 PrintUtil.println(System.err, System.lineSeparator()+System.lineSeparator());
509 PrintUtil.println(System.err, "PERF: GATT primary-services completed"+System.lineSeparator()+
510 "PERF: adapter-init to discovered " + td00 + " ms,"+System.lineSeparator()+
511 "PERF: adapter-init to processing-start " + td01 + " ms,"+System.lineSeparator()+
512 "PERF: adapter-init to gatt-complete " + td05 + " ms,"+System.lineSeparator()+
513 "PERF: discovered to processing-start " + tdc1 + " ms,"+System.lineSeparator()+
514 "PERF: discovered to gatt-complete " + tdc5 + " ms,"+System.lineSeparator()+
515 "PERF: SMPKeyBin + LE_PHY " + td13 + " ms (SMPKeyBin "+td12+"ms, LE_PHY "+td23+"ms), "+System.lineSeparator()+
516 "PERF: get-gatt-services " + td35 + " ms,"+System.lineSeparator());
517 }
518
519 {
520 final int response_size = resp_exp.length;
521 int fail_point = 0;
522 final BTGattCmd cmd = new BTGattCmd(device, "TestCmd", null /* service_uuid */, cmd_req_uuid, cmd_rsp_uuid);
523 cmd.setVerbose(true);
524 HCIStatusCode cmd_res = cmd.isResolved() ? HCIStatusCode.SUCCESS : HCIStatusCode.INTERNAL_FAILURE;
525 if( HCIStatusCode.SUCCESS != cmd_res ) {
526 fail_point = 1;
527 } else {
528 cmd.setResponseMinSize(response_size);
529 cmd.setDataCallback( (final BTGattChar charDecl, final byte[] value, final long timestampMS) -> {
530 PrintUtil.fprintf_td(System.err, "Received: size %d, %s\n", value.length, BasicTypes.bytesHexString(value, 0, -1, true));
531 });
532 PrintUtil.println(System.err, "Command test: "+cmd.toString());
533 cmd_res = cmd.sendOnly(true /* prefNoAck */, cmd_data1);
534 }
535 if( HCIStatusCode.SUCCESS != cmd_res ) {
536 fail_point = 2;
537 } else {
538 cmd_res = cmd.send(true /* prefNoAck */, cmd_data2, 3000 /* timeoutMS */);
539 }
540 if( HCIStatusCode.SUCCESS != cmd_res ) {
541 fail_point = 3;
542 } else {
543 if( !cmd.hasResponseSet() ) {
544 fail_point = 4;
545 } else {
546 final byte[] resp = cmd.getResponse();
547 if( response_size != resp.length ) {
548 fail_point = 5;
549 PrintUtil.fprintf_td(System.err, "Success: %s -> %s (response size)\n", cmd.toString(), BasicTypes.bytesHexString(resp, 0, resp.length, true /* lsb */));
550 } else if( !Arrays.equals(resp_exp, resp) ) {
551 fail_point = 6;
552 PrintUtil.fprintf_td(System.err, "Failure: %s (response content)\n", cmd.toString());
553 PrintUtil.fprintf_td(System.err, "- exp %s\n", BasicTypes.bytesHexString(resp_exp, 0, resp_exp.length, true /* lsb */));
554 PrintUtil.fprintf_td(System.err, "- has %s\n", BasicTypes.bytesHexString(resp, 0, resp.length, true /* lsb */));
555 } else {
556 PrintUtil.fprintf_td(System.err, "Success: %s -> %s\n", cmd.toString(), BasicTypes.bytesHexString(resp, 0, resp.length, true /* lsb */));
557 }
558 }
559 }
560 if( 0 != fail_point ) {
561 PrintUtil.fprintf_td(System.err, "Failure: point %d: %s -> %s\n", fail_point, cmd.toString(), cmd_res.toString());
562 }
563 cmd.close();
564 }
565
566 try {
567 int i=0;
568 for(final Iterator<BTGattService> srvIter = primServices.iterator(); srvIter.hasNext(); i++) {
569 final BTGattService primService = srvIter.next();
570 {
571 PrintUtil.fprintf_td(System.err, " [%02d] Service UUID %s\n", i, primService.getUUID());
572 PrintUtil.fprintf_td(System.err, " [%02d] %s\n", i, primService.toString());
573 }
574 int j=0;
575 final List<BTGattChar> serviceCharacteristics = primService.getChars();
576 for(final Iterator<BTGattChar> charIter = serviceCharacteristics.iterator(); charIter.hasNext(); j++) {
577 final BTGattChar serviceChar = charIter.next();
578 {
579 PrintUtil.fprintf_td(System.err, " [%02d.%02d] Characteristic: UUID %s\n", i, j, serviceChar.getUUID());
580 PrintUtil.fprintf_td(System.err, " [%02d.%02d] %s\n", i, j, serviceChar.toString());
581 }
582 final GattCharPropertySet properties = serviceChar.getProperties();
583 if( properties.isSet(GattCharPropertySet.Type.Read) ) {
584 final byte[] value = serviceChar.readValue();
585 final String svalue = BTUtils.decodeUTF8String(value, 0, value.length);
586 {
587 PrintUtil.fprintf_td(System.err, " [%02d.%02d] value: %s ('%s')\n", i, j, BasicTypes.bytesHexString(value, 0, -1, true), svalue);
588 }
589 }
590 int k=0;
591 final List<BTGattDesc> charDescList = serviceChar.getDescriptors();
592 for(final Iterator<BTGattDesc> descIter = charDescList.iterator(); descIter.hasNext(); k++) {
593 final BTGattDesc charDesc = descIter.next();
594 {
595 PrintUtil.fprintf_td(System.err, " [%02d.%02d.%02d] Descriptor: UUID %s\n", i, j, k, charDesc.getUUID());
596 PrintUtil.fprintf_td(System.err, " [%02d.%02d.%02d] %s\n", i, j, k, charDesc.toString());
597 }
598 }
599 final boolean cccdEnableResult[] = { false, false };
600 if( serviceChar.enableNotificationOrIndication( cccdEnableResult ) ) {
601 // ClientCharConfigDescriptor (CCD) is available
602 final boolean clAdded = serviceChar.addCharListener( new MyGATTEventListener(i, j) );
603 {
604 PrintUtil.fprintf_td(System.err, " [%02d.%02d] Characteristic-Listener: Notification(%b), Indication(%b): Added %b\n",
605 i, j, cccdEnableResult[0], cccdEnableResult[1], clAdded);
606 PrintUtil.fprintf_td(System.err, "\n");
607 }
608 }
609 }
610 PrintUtil.fprintf_td(System.err, "\n");
611 }
612 } catch( final Exception ex) {
613 PrintUtil.println(System.err, "Caught "+ex.getMessage());
614 ex.printStackTrace();
615 }
616 // FIXME sleep 1s for potential callbacks ..
617 try {
618 Thread.sleep(1000);
619 } catch (final InterruptedException e) {
620 e.printStackTrace();
621 }
622 success = true;
623 } catch (final Throwable t ) {
624 PrintUtil.println(System.err, "****** Processing Ready Device: Exception caught for " + device.toString() + ": "+t.getMessage());
625 t.printStackTrace();
626 }
627
628 PrintUtil.println(System.err, "****** Processing Ready Device: End-1: Success " + success + " on " + device.toString());
629
630 if( DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_DISCONNECTED == discoveryPolicy ) {
632 }
633
634 if( KEEP_CONNECTED && GATT_PING_ENABLED && success ) {
635 while( device.pingGATT() ) {
636 PrintUtil.println(System.err, "****** Processing Ready Device: pingGATT OK: "+device.getAddressAndType());
637 try {
638 Thread.sleep(1000);
639 } catch (final InterruptedException e) {
640 e.printStackTrace();
641 }
642 }
643 PrintUtil.println(System.err, "****** Processing Ready Device: pingGATT failed, waiting for disconnect: "+device.getAddressAndType());
644 // Even w/ GATT_PING_ENABLED, we utilize disconnect event to clean up -> remove
645 }
646
647 PrintUtil.println(System.err, "****** Processing Ready Device: End-2: Success " + success + " on " + device.toString());
648 if( success ) {
650 }
651 device.removeAllCharListener();
652
653 if( !KEEP_CONNECTED ) {
654
655 device.remove();
656
657 if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount.get() % RESET_ADAPTER_EACH_CONN ) {
658 resetAdapter(device.getAdapter(), 2);
659 }
660 }
661
662 if( 0 < MULTI_MEASUREMENTS.get() ) {
663 MULTI_MEASUREMENTS.decrementAndGet();
664 PrintUtil.println(System.err, "****** Processing Ready Device: MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS.get()+": "+device.getAddressAndType());
665 }
666 }
667
668 private void removeDevice(final BTDevice device) {
669 PrintUtil.println(System.err, "****** Remove Device: removing: "+device.getAddressAndType());
670
671 device.remove();
672 }
673
674 private void resetAdapter(final BTAdapter adapter, final int mode) {
675 PrintUtil.println(System.err, "****** Reset Adapter: reset["+mode+"] start: "+adapter.toString());
676 final HCIStatusCode res = adapter.reset();
677 PrintUtil.println(System.err, "****** Reset Adapter: reset["+mode+"] end: "+res+", "+adapter.toString());
678 }
679
680 DiscoveryPolicy discoveryPolicy = DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_READY; // default value
681 boolean le_scan_active = true; // default value
682 static final short le_scan_interval = (short)24; // default value
683 static final short le_scan_window = (short)24; // default value
684 static final byte filter_policy = (byte)0; // default value
685 static final boolean filter_dup = true; // default value
686
687 @SuppressWarnings("unused")
688 private boolean startDiscovery(final BTAdapter adapter, final String msg) {
689 if( !useAdapter.equals(EUI48.ALL_DEVICE) && !useAdapter.equals(adapter.getAddressAndType().address) ) {
690 PrintUtil.fprintf_td(System.err, "****** Start discovery (%s): Adapter not selected: %s\n", msg, adapter.toString());
691 return false;
692 }
693
694 if( false ) {
696 if( null != gattDevNameChar ) {
697 final byte[] aname_bytes = adapter.getName().getBytes(StandardCharsets.UTF_8);
698 gattDevNameChar.setValue(aname_bytes, 0, aname_bytes.length, 0);
699 }
700 }
701
702 final HCIStatusCode status = adapter.startDiscovery(dbGattServer, discoveryPolicy, le_scan_active, le_scan_interval, le_scan_window, filter_policy, filter_dup );
703 PrintUtil.println(System.err, "****** Start discovery ("+msg+") result: "+status);
704 PrintUtil.println(System.err, dbGattServer.toFullString());
705 return HCIStatusCode.SUCCESS == status;
706 }
707
708 private boolean initAdapter(final BTAdapter adapter) {
709 if( !useAdapter.equals(EUI48.ALL_DEVICE) && !useAdapter.equals(adapter.getAddressAndType().address) ) {
710 PrintUtil.fprintf_td(System.err, "initAdapter: Adapter not selected: %s\n", adapter.toString());
711 return false;
712 }
713 // Initialize with defaults and power-on
714 if( !adapter.isInitialized() ) {
715 final HCIStatusCode status = adapter.initialize( btMode, true );
716 if( HCIStatusCode.SUCCESS != status ) {
717 PrintUtil.fprintf_td(System.err, "initAdapter: Adapter initialization failed: %s: %s\n",
718 status.toString(), adapter.toString());
719 return false;
720 }
721 } else if( !adapter.setPowered( true ) ) {
722 PrintUtil.fprintf_td(System.err, "initAdapter: Already initialized adapter power-on failed:: %s\n", adapter.toString());
723 return false;
724 }
725 // adapter is powered-on
726 PrintUtil.fprintf_td(System.err, "initAdapter: %s\n", adapter.toString());
727 {
728 final LE_Features le_feats = adapter.getLEFeatures();
729 PrintUtil.fprintf_td(System.err, "initAdapter: LE_Features %s\n", le_feats.toString());
730 }
731 if( adapter.getBTMajorVersion() > 4 ) {
732 final LE_PHYs Tx = new LE_PHYs(LE_PHYs.PHY.LE_2M);
733 final LE_PHYs Rx = new LE_PHYs(LE_PHYs.PHY.LE_2M);
734
735 final HCIStatusCode res = adapter.setDefaultLE_PHY(Tx, Rx);
736 PrintUtil.fprintf_td(System.err, "initAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n",
737 res.toString(), Tx.toString(), Rx.toString());
738 }
739 final AdapterStatusListener asl = new MyAdapterStatusListener();
740 adapter.addStatusListener( asl );
741
742 if( !startDiscovery(adapter, "initAdapter") ) {
743 adapter.removeStatusListener( asl );
744 return false;
745 }
746 return true;
747 }
748
749 private final BTManager.ChangedAdapterSetListener myChangedAdapterSetListener =
751 @Override
752 public void adapterAdded(final BTAdapter adapter) {
753 if( null == chosenAdapter ) {
754 if( initAdapter( adapter ) ) {
755 chosenAdapter = adapter;
756 PrintUtil.println(System.err, "****** Adapter ADDED__: InitOK: " + adapter);
757 } else {
758 PrintUtil.println(System.err, "****** Adapter ADDED__: Ignored: " + adapter);
759 }
760 } else {
761 PrintUtil.println(System.err, "****** Adapter ADDED__: Ignored (other): " + adapter);
762 }
763 }
764
765 @Override
766 public void adapterRemoved(final BTAdapter adapter) {
767 if( null != chosenAdapter && adapter == chosenAdapter ) {
768 chosenAdapter = null;
769 PrintUtil.println(System.err, "****** Adapter REMOVED: " + adapter);
770 } else {
771 PrintUtil.println(System.err, "****** Adapter REMOVED (other): " + adapter);
772 }
773 }
774 };
775
776 public void runTest(final BTManager manager) {
777 timestamp_t0 = Clock.currentTimeMillis();
778
779 boolean done = false;
780
781 manager.addChangedAdapterSetListener(myChangedAdapterSetListener);
782
783 while( !done ) {
784 if( 0 == MULTI_MEASUREMENTS.get() ||
785 ( -1 == MULTI_MEASUREMENTS.get() && !BTDeviceRegistry.isWaitingForAnyDevice() && BTDeviceRegistry.areAllDevicesProcessed() )
786 )
787 {
788 PrintUtil.println(System.err, "****** EOL Test MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS.get()+
790 PrintUtil.println(System.err, "****** WaitForDevices "+BTDeviceRegistry.getWaitForDevicesString());
791 PrintUtil.println(System.err, "****** DevicesProcessed "+BTDeviceRegistry.getProcessedDevicesString());
792 done = true;
793 } else {
794 try {
795 Thread.sleep(2000);
796 } catch (final InterruptedException e) {
797 e.printStackTrace();
798 }
799 }
800 }
801 chosenAdapter = null;
802
803 //
804 // just a manually controlled pull down to show status, not required
805 //
806 final List<BTAdapter> adapters = manager.getAdapters();
807
808 adapters.forEach(new Consumer<BTAdapter>() {
809 @Override
810 public void accept(final BTAdapter a) {
811 PrintUtil.println(System.err, "****** EOL Adapter's Devices - pre_ close: " + a);
812 } } );
813 {
814 final int count = manager.removeChangedAdapterSetListener(myChangedAdapterSetListener);
815 PrintUtil.println(System.err, "****** EOL Removed ChangedAdapterSetCallback " + count);
816
817 // All implicit via destructor or shutdown hook!
818 manager.shutdown(); /* implies: adapter.close(); */
819 }
820 adapters.forEach(new Consumer<BTAdapter>() {
821 @Override
822 public void accept(final BTAdapter a) {
823 PrintUtil.println(System.err, "****** EOL Adapter's Devices - post close: " + a);
824 } } );
825
826 }
827
828 public static void main(final String[] args) throws InterruptedException {
829 for(int i=0; i< args.length; i++) {
830 final String arg = args[i];
831 if( arg.equals("-debug") ) {
832 System.setProperty("org.tinyb.verbose", "true");
833 System.setProperty("org.tinyb.debug", "true");
834 } else if( arg.equals("-verbose") ) {
835 System.setProperty("org.tinyb.verbose", "true");
836 } else if( arg.equals("-dbt_debug") && args.length > (i+1) ) {
837 System.setProperty("direct_bt.debug", args[++i]);
838 } else if( arg.equals("-dbt_verbose") && args.length > (i+1) ) {
839 System.setProperty("direct_bt.verbose", args[++i]);
840 } else if( arg.equals("-dbt_gatt") && args.length > (i+1) ) {
841 System.setProperty("direct_bt.gatt", args[++i]);
842 } else if( arg.equals("-dbt_l2cap") && args.length > (i+1) ) {
843 System.setProperty("direct_bt.l2cap", args[++i]);
844 } else if( arg.equals("-dbt_hci") && args.length > (i+1) ) {
845 System.setProperty("direct_bt.hci", args[++i]);
846 } else if( arg.equals("-dbt_mgmt") && args.length > (i+1) ) {
847 System.setProperty("direct_bt.mgmt", args[++i]);
848 }
849 }
850 final BTManager manager;
851 try {
852 manager = BTFactory.getDirectBTManager();
853 } catch (BTException | NoSuchMethodException | SecurityException
854 | IllegalAccessException | IllegalArgumentException
855 | InvocationTargetException | ClassNotFoundException e) {
856 System.err.println("Unable to instantiate DirectBT BluetoothManager");
857 e.printStackTrace();
858 System.exit(-1);
859 return;
860 }
861 PrintUtil.println(System.err, "Direct-BT BluetoothManager initialized!");
862 PrintUtil.println(System.err, "Direct-BT Native Version "+BTFactory.getNativeVersion()+" (API "+BTFactory.getNativeAPIVersion()+")");
863 PrintUtil.println(System.err, "Direct-BT Java Version "+BTFactory.getImplVersion()+" (API "+BTFactory.getAPIVersion()+")");
864
865 final DBTLabPadClient01 test = new DBTLabPadClient01();
866
867 // Add defaults for Avalun's LabPad device, announcing its device name as 'LabPad[0-9]+'
868 final String dev_name_prefix = "LabPad";
869 {
870 BTDeviceRegistry.addToWaitForDevices( dev_name_prefix );
871 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getOrCreate(dev_name_prefix);
872 sec.io_cap = SMPIOCapability.KEYBOARD_ONLY;
873 sec.sec_level = BTSecurityLevel.ENC_AUTH;
874 }
875
876 boolean waitForEnter=false;
877 {
878 for(int i=0; i< args.length; i++) {
879 final String arg = args[i];
880
881 if( arg.equals("-wait") ) {
882 waitForEnter = true;
883 } else if( arg.equals("-show_update_events") ) {
884 test.SHOW_UPDATE_EVENTS = true;
885 } else if( arg.equals("-quiet") ) {
886 test.QUIET = true;
887 } else if( arg.equals("-shutdown") && args.length > (i+1) ) {
888 test.shutdownTest = Integer.valueOf(args[++i]).intValue();
889 } else if( arg.equals("-discoveryPolicy") && args.length > (i+1) ) {
890 test.discoveryPolicy = DiscoveryPolicy.get((byte) Integer.valueOf(args[++i]).intValue() );
891 } else if( arg.equals("-scanPassive") ) {
892 test.le_scan_active = false;
893 } else if( arg.equals("-btmode") && args.length > (i+1) ) {
894 test.btMode = BTMode.get(args[++i]);
895 } else if( arg.equals("-adapter") && args.length > (i+1) ) {
896 test.useAdapter = new EUI48( args[++i] );
897 } else if( arg.equals("-passkey") && args.length > (i+1) ) {
898 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getOrCreate(dev_name_prefix);
899 sec.passkey = Integer.valueOf(args[++i]).intValue();
900 System.err.println("Set passkey in "+sec);
901 }
902 }
903 PrintUtil.println(System.err, "Run with '[-btmode LE|BREDR|DUAL] "+
904 "[-disconnect] [-show_update_events] [-quiet] "+
905 "[-discoveryPolicy <0-4>] "+
906 "[-scanPassive] "+
907 "[-adapter <adapter_address>] "+
908 "(-passkey <digits>)* "+
909 "[-verbose] [-debug] "+
910 "[-dbt_verbose true|false] "+
911 "[-dbt_debug true|false|adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event] "+
912 "[-dbt_mgmt cmd.timeout=3000,ringsize=64,...] "+
913 "[-dbt_hci cmd.complete.timeout=10000,cmd.status.timeout=3000,ringsize=64,...] "+
914 "[-dbt_gatt cmd.read.timeout=500,cmd.write.timeout=500,cmd.init.timeout=2500,ringsize=128,...] "+
915 "[-dbt_l2cap reader.timeout=10000,restart.count=0,...] ");
916 }
917
918 PrintUtil.println(System.err, "MULTI_MEASUREMENTS "+test.MULTI_MEASUREMENTS.get());
919 PrintUtil.println(System.err, "KEEP_CONNECTED "+test.KEEP_CONNECTED);
920 PrintUtil.println(System.err, "RESET_ADAPTER_EACH_CONN "+test.RESET_ADAPTER_EACH_CONN);
921 PrintUtil.println(System.err, "GATT_PING_ENABLED "+test.GATT_PING_ENABLED);
922 PrintUtil.println(System.err, "REMOVE_DEVICE "+test.REMOVE_DEVICE);
923 PrintUtil.println(System.err, "SHOW_UPDATE_EVENTS "+test.SHOW_UPDATE_EVENTS);
924 PrintUtil.println(System.err, "QUIET "+test.QUIET);
925 PrintUtil.println(System.err, "adapter "+test.useAdapter);
926 PrintUtil.println(System.err, "btmode "+test.btMode.toString());
927 PrintUtil.println(System.err, "discoveryPolicy "+test.discoveryPolicy.toString());
928 PrintUtil.println(System.err, "le_scan_active "+test.le_scan_active);
929 PrintUtil.println(System.err, "security-details: "+BTSecurityRegistry.allToString() );
930 PrintUtil.println(System.err, "waitForDevices: "+BTDeviceRegistry.getWaitForDevicesString());
931
932 if( waitForEnter ) {
933 PrintUtil.println(System.err, "Press ENTER to continue\n");
934 try{ System.in.read();
935 } catch(final Exception e) { }
936 }
937 test.runTest(manager);
938 }
939}
Author: Sven Gothel sgothel@jausoft.com Copyright (c) 2021 Gothel Software e.K.
static final String CLIENT_KEY_PATH
This Java example demonstrates a client connecting to Avalun's LabPad device.
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
synchronized HCIStatusCode sendOnly(final boolean prefNoAck, final byte[] cmd_data)
Send the command to the remote BTDevice, only.
Definition: BTGattCmd.java:376
void setResponseMinSize(final int v)
Definition: BTGattCmd.java:299
void setDataCallback(final DataCallback dcb)
Definition: BTGattCmd.java:300
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
Selected standard GATT characteristic numbers in UUID16 format as defined.
Definition: DBGattChar.java:56
static String SOFTWARE_REVISION_STRING
Definition: DBGattChar.java:80
static String MANUFACTURER_NAME_STRING
Definition: DBGattChar.java:81
static String FIRMWARE_REVISION_STRING
Definition: DBGattChar.java:78
static String HARDWARE_REVISION_STRING
Definition: DBGattChar.java:79
Representing a Gatt Characteristic object from the GATT server perspective.
Definition: DBGattChar.java:46
native boolean setValue(final byte[] source, final int source_pos, final int source_len, final int dest_pos)
Set this characteristic's native value.
Representing a complete list of Gatt Service objects from the GATT server perspective,...
DBGattChar findGattChar(final String service_uuid, final String char_uuid)
Selected standard GATT service service numbers in UUID16 format as defined.
static String GENERIC_ACCESS
This service contains generic information about the device.
static String DEVICE_INFORMATION
This service exposes manufacturer and/or vendor information about a device.
Representing a Gatt Service object from the ::GATTRole::Server perspective.
A copy of the native GATT value of DBGattChar or DBGattDesc.
static DBGattValue make(final String name)
Convenience DBGattValue ctor function.
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.
ENC_AUTH
Encryption and authentication (MITM).
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.
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.
String getName()
Returns the name.
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.