Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
DBTPeripheral00.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2021 Gothel Software e.K.
4 * Copyright (c) 2021 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;
32
33import org.direct_bt.AdapterSettings;
34import org.direct_bt.AdapterStatusListener;
35import org.direct_bt.BDAddressAndType;
36import org.direct_bt.BTMode;
37import org.direct_bt.BTSecurityLevel;
38import org.direct_bt.BTAdapter;
39import org.direct_bt.BTDevice;
40import org.direct_bt.BTException;
41import org.direct_bt.BTFactory;
42import org.direct_bt.BTManager;
43import org.direct_bt.BTSecurityRegistry;
44import org.direct_bt.BTUtils;
45import org.direct_bt.DBGattChar;
46import org.direct_bt.DBGattDesc;
47import org.direct_bt.DBGattServer;
48import org.direct_bt.DBGattService;
49import org.direct_bt.DBGattValue;
50import org.direct_bt.DiscoveryPolicy;
51import org.direct_bt.EIRDataTypeSet;
52import org.direct_bt.EInfoReport;
53import org.direct_bt.GAPFlags;
54import org.direct_bt.GattCharPropertySet;
55import org.direct_bt.HCIStatusCode;
56import org.direct_bt.LE_Features;
57import org.direct_bt.LE_PHYs;
58import org.direct_bt.PairingMode;
59import org.direct_bt.SMPIOCapability;
60import org.direct_bt.SMPPairingState;
61import org.direct_bt.ScanType;
62import org.jau.io.PrintUtil;
63import org.jau.net.EUI48;
64import org.jau.sys.Clock;
65import org.jau.util.BasicTypes;
66
67/**
68 * This Java peripheral {@link BTRole::Slave} GATT server example uses an event driven workflow.
69 * <p>
70 * See `dbt_peripheral00.cpp` for invocation examples, since both apps are fully compatible.
71 * </p>
72 */
73public class DBTPeripheral00 {
74 long timestamp_t0;
75
76 EUI48 useAdapter = EUI48.ALL_DEVICE;
77 BTMode btMode = BTMode.DUAL;
78 BTAdapter chosenAdapter = null;
79
80 boolean use_SC = true;
81 String adapter_name = "TestDev001_J";
82 String adapter_short_name = "TDev001J";
83 BTSecurityLevel adapter_sec_level = BTSecurityLevel.UNSET;
84 SMPIOCapability adapter_sec_io_cap = SMPIOCapability.UNSET;
85 boolean SHOW_UPDATE_EVENTS = false;
86 boolean RUN_ONLY_ONCE = false;
87 private final Object sync_lock = new Object();
88 private volatile BTDevice connectedDevice;
89
90 volatile int servedConnections = 0;
91
92 private final void setDevice(final BTDevice cd) {
93 synchronized( sync_lock ) {
94 connectedDevice = cd;
95 }
96 }
97
98 private final BTDevice getDevice() {
99 synchronized( sync_lock ) {
100 return connectedDevice;
101 }
102 }
103
104 private boolean matches(final BTDevice device) {
105 final BTDevice d = getDevice();
106 return null != d ? d.equals(device) : false;
107 }
108 boolean matches(final List<BDAddressAndType> cont, final BDAddressAndType mac) {
109 for(final Iterator<BDAddressAndType> it = cont.iterator(); it.hasNext(); ) {
110 if( it.next().matches(mac) ) {
111 return true;
112 }
113 }
114 return false;
115 }
116
117 static Thread executeOffThread(final Runnable runobj, final String threadName, final boolean detach) {
118 final Thread t = new Thread( runobj, threadName );
119 if( detach ) {
120 t.setDaemon(true); // detach thread
121 }
122 t.start();
123 return t;
124 }
125 static Thread executeOffThread(final Runnable runobj, final boolean detach) {
126 final Thread t = new Thread( runobj );
127 if( detach ) {
128 t.setDaemon(true); // detach thread
129 }
130 t.start();
131 return t;
132 }
133
134 static String DataServiceUUID = "d0ca6bf3-3d50-4760-98e5-fc5883e93712";
135 static String StaticDataUUID = "d0ca6bf3-3d51-4760-98e5-fc5883e93712";
136 static String CommandUUID = "d0ca6bf3-3d52-4760-98e5-fc5883e93712";
137 static String ResponseUUID = "d0ca6bf3-3d53-4760-98e5-fc5883e93712";
138 static String PulseDataUUID = "d0ca6bf3-3d54-4760-98e5-fc5883e93712";
139
140 // DBGattServerRef dbGattServer = std::make_shared<DBGattServer>(
141 private final DBGattServer dbGattServer = new DBGattServer(
142 /* services: */
143 Arrays.asList( // DBGattService
144 new DBGattService ( true /* primary */,
146 Arrays.asList( // DBGattChar
147 new DBGattChar( DBGattChar.UUID16.DEVICE_NAME /* value_type_ */,
149 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
150 DBGattValue.make(adapter_name, 128) /* value */ ),
151 new DBGattChar( DBGattChar.UUID16.APPEARANCE /* value_type_ */,
153 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
154 DBGattValue.make((short)0) /* value */ )
155 ) ),
156 new DBGattService ( true /* primary */,
158 Arrays.asList( // DBGattChar
161 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
162 DBGattValue.make("Gothel Software") /* value */ ),
163 new DBGattChar( DBGattChar.UUID16.MODEL_NUMBER_STRING /* value_type_ */,
165 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
166 DBGattValue.make("2.4.0-pre") /* value */ ),
167 new DBGattChar( DBGattChar.UUID16.SERIAL_NUMBER_STRING /* value_type_ */,
169 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
170 DBGattValue.make("sn:0123456789") /* value */ ),
173 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
174 DBGattValue.make("hw:0123456789") /* value */ ),
177 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
178 DBGattValue.make("fw:0123456789") /* value */ ),
181 new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ),
182 DBGattValue.make("sw:0123456789") /* value */ )
183 ) ),
184 new DBGattService ( true /* primary */,
185 DataServiceUUID /* type_ */,
186 Arrays.asList( // DBGattChar
187 new DBGattChar( StaticDataUUID /* value_type_ */,
189 Arrays.asList( // DBGattDesc
191 ),
192 DBGattValue.make("Proprietary Static Data 0x00010203") /* value */ ),
193 new DBGattChar( CommandUUID /* value_type_ */,
195 Arrays.asList( // DBGattDesc
197 ),
198 DBGattValue.make(128, 64) /* value */ ),
199 new DBGattChar( ResponseUUID /* value_type_ */,
201 Arrays.asList( // DBGattDesc
204 ),
205 DBGattValue.make((short)0) /* value */ ),
206 new DBGattChar( PulseDataUUID /* value_type_ */,
208 Arrays.asList( // DBGattDesc
211 ),
212 DBGattValue.make("Synthethic Sensor 01") /* value */ )
213 ) )
214 ) );
215
216
217 class MyAdapterStatusListener extends AdapterStatusListener {
218 @Override
219 public void adapterSettingsChanged(final BTAdapter adapter, final AdapterSettings oldmask,
220 final AdapterSettings newmask, final AdapterSettings changedmask, final long timestamp) {
221 final boolean initialSetting = oldmask.isEmpty();
222 if( initialSetting ) {
223 PrintUtil.println(System.err, "****** SETTINGS: "+oldmask+" -> "+newmask+", initial "+changedmask);
224 } else {
225 PrintUtil.println(System.err, "****** SETTINGS: "+oldmask+" -> "+newmask+", changed "+changedmask);
226 }
227 PrintUtil.println(System.err, "Status Adapter:");
228 PrintUtil.println(System.err, adapter.toString());
229 if( !initialSetting &&
232 {
233
234 executeOffThread( () -> { startAdvertising(adapter, "powered-on"); },
235 "DBT-StartAdvertising-"+adapter.getAddressAndType(), true /* detach */);
236 }
237 }
238
239 @Override
240 public void discoveringChanged(final BTAdapter adapter, final ScanType currentMeta, final ScanType changedType, final boolean changedEnabled, final DiscoveryPolicy policy, final long timestamp) {
241 PrintUtil.println(System.err, "****** DISCOVERING: meta "+currentMeta+", changed["+changedType+", enabled "+changedEnabled+", policy "+policy+"] on "+adapter);
242 }
243
244 @Override
245 public boolean deviceFound(final BTDevice device, final long timestamp) {
246 PrintUtil.println(System.err, "****** FOUND__-1: NOP "+device.toString());
247 return false;
248 }
249
250 @Override
251 public void deviceUpdated(final BTDevice device, final EIRDataTypeSet updateMask, final long timestamp) {
252 if( updateMask.isSet(EIRDataTypeSet.DataType.BDADDR) ) {
253 PrintUtil.println(System.err, "****** UPDATED (ADDR-RESOLVED): "+updateMask+" of "+device);
254 } else if( SHOW_UPDATE_EVENTS ) {
255 PrintUtil.println(System.err, "****** UPDATED: "+updateMask+" of "+device);
256 }
257 }
258
259 @Override
260 public void deviceConnected(final BTDevice device, final boolean discovered, final long timestamp) {
261 PrintUtil.println(System.err, "****** CONNECTED (discovered "+discovered+"): "+device.toString());
262 final boolean available = null == getDevice();
263 if( available ) {
264 setDevice(device);
265 }
266 }
267
268 @Override
269 public void devicePairingState(final BTDevice device, final SMPPairingState state, final PairingMode mode, final long timestamp) {
270 PrintUtil.println(System.err, "****** PAIRING_STATE: state "+state+", mode "+mode+": "+device);
271 switch( state ) {
272 case NONE:
273 // next: deviceReady(..)
274 break;
275 case FAILED: {
276 // next: deviceReady() or deviceDisconnected(..)
277 } break;
278 case REQUESTED_BY_RESPONDER:
279 // next: FEATURE_EXCHANGE_STARTED
280 break;
281 case FEATURE_EXCHANGE_STARTED:
282 // next: FEATURE_EXCHANGE_COMPLETED
283 break;
284 case FEATURE_EXCHANGE_COMPLETED:
285 // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION
286 break;
287 case PASSKEY_EXPECTED: {
288 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, "");
289 if( null != sec && sec.getPairingPasskey() != BTSecurityRegistry.NO_PASSKEY ) {
290 executeOffThread( () -> { device.setPairingPasskey( sec.getPairingPasskey() ); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */);
291 } else {
292 executeOffThread( () -> { device.setPairingPasskey( 0 ); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */);
293 // 3s disconnect: executeOffThread( () -> { device.setPairingPasskeyNegative(); }, "DBT-SetPasskeyNegative-"+device.getAddressAndType(), true /* detach */);
294 }
295 // next: KEY_DISTRIBUTION or FAILED
296 } break;
297 case NUMERIC_COMPARE_EXPECTED: {
298 final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, "");
299 if( null != sec ) {
300 executeOffThread( () -> { device.setPairingNumericComparison( sec.getPairingNumericComparison() ); }, "DBT-SetNumericComp-"+device.getAddressAndType(), true /* detach */);
301 } else {
302 executeOffThread( () -> { device.setPairingNumericComparison( false ); }, "DBT-SetNumericCompFalse-"+device.getAddressAndType(), true /* detach */);
303 }
304 // next: KEY_DISTRIBUTION or FAILED
305 } break;
306 case PASSKEY_NOTIFY: {
307 PrintUtil.println(System.err, "****** ");
308 PrintUtil.println(System.err, "****** ");
309 PrintUtil.println(System.err, "****** Confirm on your device "+device.getName());
310 PrintUtil.println(System.err, "****** PassKey: "+device.getResponderSMPPassKeyString());
311 PrintUtil.println(System.err, "****** ");
312 PrintUtil.println(System.err, "****** ");
313 // next: KEY_DISTRIBUTION or FAILED
314 } break;
315 case OOB_EXPECTED:
316 // FIXME: ABORT
317 break;
318 case KEY_DISTRIBUTION:
319 // next: COMPLETED or FAILED
320 break;
321 case COMPLETED:
322 // next: deviceReady(..)
323 break;
324 default: // nop
325 break;
326 }
327 }
328
329 @Override
330 public void deviceReady(final BTDevice device, final long timestamp) {
331 PrintUtil.println(System.err, "****** READY-1: NOP " + device.toString());
332 }
333
334 @Override
335 public void deviceDisconnected(final BTDevice device, final HCIStatusCode reason, final short handle, final long timestamp) {
336 servedConnections++;
337 PrintUtil.println(System.err, "****** DISCONNECTED (count "+servedConnections+"): Reason "+reason+", old handle 0x"+Integer.toHexString(handle)+": "+device+" on "+device.getAdapter());
338 final boolean match = matches(device);
339 if( match ) {
340 setDevice(null);
341 }
342 executeOffThread( () -> { processDisconnectedDevice(device); },
343 "DBT-Disconnected-"+device.getAdapter().getAddressAndType(), true /* detach */);
344 }
345
346 @Override
347 public String toString() {
348 return "AdapterStatusListener[user, per-adapter]";
349 }
350 };
351
352 class MyGATTServerListener extends DBGattServer.Listener {
353 private final Thread pulseSenderThread;
354 private volatile boolean stopPulseSenderFlag = false;
355
356 private volatile short handlePulseDataNotify = 0;
357 private volatile short handlePulseDataIndicate = 0;
358 private volatile short handleResponseDataNotify = 0;
359 private volatile short handleResponseDataIndicate = 0;
360
361 private int usedMTU = 23; // BTGattHandler::number(BTGattHandler::Defaults::MIN_ATT_MTU);
362
363 private void clear() {
364 synchronized( sync_lock ) {
365 handlePulseDataNotify = 0;
366 handlePulseDataIndicate = 0;
367 handleResponseDataNotify = 0;
368 handleResponseDataIndicate = 0;
369
370 dbGattServer.resetGattClientCharConfig(DataServiceUUID, PulseDataUUID);
371 dbGattServer.resetGattClientCharConfig(DataServiceUUID, ResponseUUID);
372 }
373 }
374
375 private boolean shallStopPulseSender() {
376 synchronized( sync_lock ) {
377 return stopPulseSenderFlag;
378 }
379 }
380 private void pulseSender() {
381 while( !shallStopPulseSender() ) {
382 {
383 final BTDevice connectedDevice_ = getDevice();
384 if( null != connectedDevice_ && connectedDevice_.getConnected() ) {
385 if( 0 != handlePulseDataNotify || 0 != handlePulseDataIndicate ) {
386 final String data = String.format("Dynamic Data Example. Elapsed Milliseconds: %,9d", Clock.elapsedTimeMillis());
387 final byte[] v = data.getBytes(StandardCharsets.UTF_8);
388 if( 0 != handlePulseDataNotify ) {
389 final boolean res = connectedDevice_.sendNotification(handlePulseDataNotify, v);
390 PrintUtil.fprintf_td(System.err, "****** GATT::sendNotification: PULSE (res %b) to %s\n", res, connectedDevice_.toString());
391 }
392 if( 0 != handlePulseDataIndicate ) {
393 final boolean res = connectedDevice_.sendIndication(handlePulseDataIndicate, v);
394 PrintUtil.fprintf_td(System.err, "****** GATT::sendIndication: PULSE (res %b) to %s\n", res, connectedDevice_.toString());
395 }
396 }
397 }
398 }
399 if( shallStopPulseSender() ) {
400 return;
401 }
402 try {
403 Thread.sleep(100); // 100ms
404 } catch (final InterruptedException e) { }
405 }
406 }
407
408 void sendResponse(final byte[] data) {
409 final BTDevice connectedDevice_ = getDevice();
410 if( null != connectedDevice_ && connectedDevice_.getConnected() ) {
411 if( 0 != handleResponseDataNotify || 0 != handleResponseDataIndicate ) {
412 if( 0 != handleResponseDataNotify ) {
413 final boolean res = connectedDevice_.sendNotification(handleResponseDataNotify, data);
414 PrintUtil.fprintf_td(System.err, "****** GATT::sendNotification (res %b): %s to %s\n",
415 res, BasicTypes.bytesHexString(data, 0, data.length, true /* lsb */), connectedDevice_.toString());
416 }
417 if( 0 != handleResponseDataIndicate ) {
418 final boolean res = connectedDevice_.sendIndication(handleResponseDataIndicate, data);
419 PrintUtil.fprintf_td(System.err, "****** GATT::sendIndication (res %b): %s to %s\n",
420 res, BasicTypes.bytesHexString(data, 0, data.length, true /* lsb */), connectedDevice_.toString());
421 }
422 }
423 }
424 }
425
426 public MyGATTServerListener() {
427 pulseSenderThread = executeOffThread( () -> { pulseSender(); }, "GattServer-PulseSender", false /* detach */);
428 }
429
430 @Override
431 public void close() {
432 synchronized( sync_lock ) {
433 stopPulseSenderFlag = true;
434 }
435 if( !pulseSenderThread.isDaemon() ) {
436 try {
437 pulseSenderThread.join(1000);
438 } catch (final InterruptedException e) { }
439 }
440 super.close();
441 }
442
443 @Override
444 public void connected(final BTDevice device, final int initialMTU) {
445 final boolean match = matches(device);
446 PrintUtil.fprintf_td(System.err, "****** GATT::connected(match %b): initMTU %d, %s\n",
447 match, initialMTU, device.toString());
448 if( match ) {
449 synchronized( sync_lock ) {
450 usedMTU = initialMTU;
451 }
452 }
453 }
454
455 @Override
456 public void disconnected(final BTDevice device) {
457 final boolean match = matches(device);
458 PrintUtil.fprintf_td(System.err, "****** GATT::disconnected(match %b): %s\n", match, device.toString());
459 if( match ) {
460 clear();
461 }
462 }
463
464 @Override
465 public void mtuChanged(final BTDevice device, final int mtu) {
466 final boolean match = matches(device);
467 PrintUtil.fprintf_td(System.err, "****** GATT::mtuChanged(match %b): %d -> %d, %s\n",
468 match, match ? (int)usedMTU : 0, mtu, device.toString());
469 if( match ) {
470 synchronized( sync_lock ) {
471 usedMTU = mtu;
472 }
473 }
474 }
475
476 @Override
477 public boolean readCharValue(final BTDevice device, final DBGattService s, final DBGattChar c) {
478 final boolean match = matches(device);
479 PrintUtil.fprintf_td(System.err, "****** GATT::readCharValue(match %b): to %s, from\n %s\n %s\n",
480 match, device.toString(), s.toString(), c.toString());
481 return match;
482 }
483
484 @Override
485 public boolean readDescValue(final BTDevice device, final DBGattService s, final DBGattChar c, final DBGattDesc d) {
486 final boolean match = matches(device);
487 PrintUtil.fprintf_td(System.err, "****** GATT::readDescValue(match %b): to %s, from\n %s\n %s\n %s\n",
488 match, device.toString(), s.toString(), c.toString(), d.toString());
489 return match;
490 }
491
492 @Override
493 public boolean writeCharValue(final BTDevice device, final DBGattService s, final DBGattChar c, final byte[] value, final int value_offset) {
494 final boolean match = matches(device);
495 final String value_s = BasicTypes.bytesHexString(value, 0, value.length, true /* lsbFirst */)+
496 " '"+BTUtils.decodeUTF8String(value, 0, value.length)+"'";
497 PrintUtil.fprintf_td(System.err, "****** GATT::writeCharValue(match %b): %s @ %d from %s, to\n %s\n %s\n",
498 match, value_s, value_offset,
499 device.toString(), s.toString(), c.toString());
500
501 return match;
502 }
503
504 @Override
505 public void writeCharValueDone(final BTDevice device, final DBGattService s, final DBGattChar c) {
506 final boolean match = matches(device);
507 final DBGattValue value = c.getValue();
508 PrintUtil.fprintf_td(System.err, "****** GATT::writeCharValueDone(match %b): From %s, to\n %s\n %s\n Char-Value: %s\n",
509 match, device.toString(), s.toString(), c.toString(), value.toString());
510
511 if( match &&
512 c.getValueType().equals( CommandUUID ) &&
513 ( 0 != handleResponseDataNotify || 0 != handleResponseDataIndicate ) )
514 {
515 executeOffThread( () -> { sendResponse( value.data() ); }, true /* detach */);
516 }
517 }
518
519 @Override
520 public boolean writeDescValue(final BTDevice device, final DBGattService s, final DBGattChar c, final DBGattDesc d,
521 final byte[] value, final int value_offset)
522 {
523 final boolean match = matches(device);
524 final String value_s = BasicTypes.bytesHexString(value, 0, value.length, true /* lsbFirst */)+
525 " '"+BTUtils.decodeUTF8String(value, 0, value.length)+"'";
526 PrintUtil.fprintf_td(System.err, "****** GATT::writeDescValue(match %b): %s @ %d from %s\n %s\n %s\n %s\n",
527 match, value_s, value_offset,
528 device.toString(), s.toString(), c.toString(), d.toString());
529 return match;
530 }
531
532 @Override
533 public void writeDescValueDone(final BTDevice device, final DBGattService s, final DBGattChar c, final DBGattDesc d) {
534 final boolean match = matches(device);
535 final DBGattValue value = d.getValue();
536 PrintUtil.fprintf_td(System.err, "****** GATT::writeDescValueDone(match %b): From %s\n %s\n %s\n %s\n Desc-Value: %s\n",
537 match, device.toString(), s.toString(), c.toString(), d.toString(), value.toString());
538 }
539
540 @Override
541 public void clientCharConfigChanged(final BTDevice device, final DBGattService s, final DBGattChar c, final DBGattDesc d,
542 final boolean notificationEnabled, final boolean indicationEnabled)
543 {
544 final boolean match = matches(device);
545 final DBGattValue value = d.getValue();
546 PrintUtil.fprintf_td(System.err, "****** GATT::clientCharConfigChanged(match %b): notify %b, indicate %b from %s\n %s\n %s\n %s\n Desc-Value: %s\n",
547 match, notificationEnabled, indicationEnabled,
548 device.toString(), s.toString(), c.toString(), d.toString(), value.toString());
549
550 if( match ) {
551 final String value_type = c.getValueType();
552 final short value_handle = c.getValueHandle();
553 if( value_type.equals( PulseDataUUID ) ) {
554 synchronized( sync_lock ) {
555 handlePulseDataNotify = notificationEnabled ? value_handle : 0;
556 handlePulseDataIndicate = indicationEnabled ? value_handle : 0;
557 }
558 } else if( value_type.equals( ResponseUUID ) ) {
559 synchronized( sync_lock ) {
560 handleResponseDataNotify = notificationEnabled ? value_handle : 0;
561 handleResponseDataIndicate = indicationEnabled ? value_handle : 0;
562 }
563 }
564 }
565 }
566 }
567 static final short adv_interval_min=(short)160; // x0.625 = 100ms
568 static final short adv_interval_max=(short)480; // x0.625 = 300ms
569 static final byte adv_type=(byte)0; // AD_PDU_Type::ADV_IND;
570 static final byte adv_chan_map=(byte)0x07;
571 static final byte filter_policy=(byte)0x00;
572
573 private boolean stopAdvertising(final BTAdapter adapter, final String msg) {
574 if( !useAdapter.equals(EUI48.ALL_DEVICE) && !useAdapter.equals(adapter.getAddressAndType().address) ) {
575 PrintUtil.fprintf_td(System.err, "****** Stop advertising (%s): Adapter not selected: %s\n", msg, adapter.toString());
576 return false;
577 }
578 final HCIStatusCode status = adapter.stopAdvertising();
579 PrintUtil.println(System.err, "****** Stop advertising ("+msg+") result: "+status+": "+adapter.toString());
580 return HCIStatusCode.SUCCESS == status;
581 }
582
583 private boolean startAdvertising(final BTAdapter adapter, final String msg) {
584 if( !useAdapter.equals(EUI48.ALL_DEVICE) && !useAdapter.equals(adapter.getAddressAndType().address) ) {
585 PrintUtil.fprintf_td(System.err, "****** Start advertising (%s): Adapter not selected: %s\n", msg, adapter.toString());
586 return false;
587 }
588
589 {
590 final LE_Features le_feats = adapter.getLEFeatures();
591 PrintUtil.fprintf_td(System.err, "initAdapter: LE_Features %s\n", le_feats.toString());
592 }
593 if( adapter.getBTMajorVersion() > 4 ) {
594 final LE_PHYs Tx = new LE_PHYs( LE_PHYs.PHY.LE_2M );
595 final LE_PHYs Rx = new LE_PHYs( LE_PHYs.PHY.LE_2M );
596 final HCIStatusCode res = adapter.setDefaultLE_PHY(Tx, Rx);
597 PrintUtil.fprintf_td(System.err, "initAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n",
598 res.toString(), Tx.toString(), Rx.toString());
599 }
600
601 adapter.setServerConnSecurity(adapter_sec_level, adapter_sec_io_cap);
602
603 final EInfoReport eir = new EInfoReport();
604 final EIRDataTypeSet adv_mask = new EIRDataTypeSet();
605 final EIRDataTypeSet scanrsp_mask = new EIRDataTypeSet();
606
609
610 scanrsp_mask.set(EIRDataTypeSet.DataType.NAME);
611 scanrsp_mask.set(EIRDataTypeSet.DataType.CONN_IVAL);
612
615
616 eir.addService(DataServiceUUID);
617 eir.setServicesComplete(false);
618
619 eir.setName(adapter.getName());
620 eir.setConnInterval((short)8, (short)12); // 10ms - 15ms
621
623 if( null != gattDevNameChar ) {
624 final byte[] aname_bytes = adapter.getName().getBytes(StandardCharsets.UTF_8);
625 gattDevNameChar.setValue(aname_bytes, 0, aname_bytes.length, 0);
626 }
627
628 PrintUtil.println(System.err, "****** Start advertising ("+msg+"): EIR "+eir.toString());
629 PrintUtil.println(System.err, "****** Start advertising ("+msg+"): adv "+adv_mask.toString()+", scanrsp "+scanrsp_mask.toString());
630
631 final HCIStatusCode status = adapter.startAdvertising(dbGattServer, eir, adv_mask, scanrsp_mask,
632 adv_interval_min, adv_interval_max,
633 adv_type, adv_chan_map, filter_policy);
634 PrintUtil.println(System.err, "****** Start advertising ("+msg+") result: "+status+": "+adapter.toString());
635 PrintUtil.println(System.err, dbGattServer.toFullString());
636 return HCIStatusCode.SUCCESS == status;
637 }
638
639 private void processDisconnectedDevice(final BTDevice device) {
640 PrintUtil.println(System.err, "****** Disconnected Device (count "+servedConnections+"): Start "+device.toString());
641
642 // already unpaired
643 stopAdvertising(device.getAdapter(), "device-disconnected");
644 device.remove();
645
646 try {
647 Thread.sleep(100); // wait a little (FIXME: Fast restart of advertising error)
648 } catch (final InterruptedException e) { }
649
650 if( !RUN_ONLY_ONCE ) {
651 startAdvertising(device.getAdapter(), "device-disconnected");
652 }
653
654 PrintUtil.println(System.err, "****** Disonnected Device: End "+device.toString());
655 }
656
657 private boolean initAdapter(final BTAdapter adapter) {
658 if( !useAdapter.equals(EUI48.ALL_DEVICE) && !useAdapter.equals(adapter.getAddressAndType().address) ) {
659 PrintUtil.fprintf_td(System.err, "initAdapter: Adapter not selected: %s\n", adapter.toString());
660 return false;
661 }
662 if( !adapter.isInitialized() ) {
663 // Initialize with defaults and power-on
664 final HCIStatusCode status = adapter.initialize( btMode, false );
665 if( HCIStatusCode.SUCCESS != status ) {
666 PrintUtil.fprintf_td(System.err, "initAdapter: initialization failed: %s: %s\n",
667 status.toString(), adapter.toString());
668 return false;
669 }
670 } else if( !adapter.setPowered( false ) ) {
671 PrintUtil.fprintf_td(System.err, "initAdapter: setPower.1 off failed: %s\n", adapter.toString());
672 return false;
673 }
674 // adapter is powered-off
675 PrintUtil.println(System.err, "initAdapter.1: "+adapter.toString());
676
677 {
678 HCIStatusCode status = adapter.setName(adapter_name, adapter_short_name);
679 if( HCIStatusCode.SUCCESS == status ) {
680 PrintUtil.fprintf_td(System.err, "initAdapter: setLocalName OK: %s\n", adapter.toString());
681 } else {
682 PrintUtil.fprintf_td(System.err, "initAdapter: setLocalName failed: %s\n", adapter.toString());
683 return false;
684 }
685
686 status = adapter.setSecureConnections( use_SC );
687 if( HCIStatusCode.SUCCESS == status ) {
688 PrintUtil.fprintf_td(System.err, "initAdapter: setSecureConnections OK: %s\n", adapter.toString());
689 } else {
690 PrintUtil.fprintf_td(System.err, "initAdapter: setSecureConnections failed: %s\n", adapter.toString());
691 return false;
692 }
693
694 final short conn_min_interval = 8; // 10ms
695 final short conn_max_interval = 40; // 50ms
696 final short conn_latency = 0;
697 final short supervision_timeout = 50; // 500ms
698 status = adapter.setDefaultConnParam(conn_min_interval, conn_max_interval, conn_latency, supervision_timeout);
699 if( HCIStatusCode.SUCCESS == status ) {
700 PrintUtil.fprintf_td(System.err, "initAdapter: setDefaultConnParam OK: %s\n", adapter.toString());
701 } else if( HCIStatusCode.UNKNOWN_COMMAND == status ) {
702 PrintUtil.fprintf_td(System.err, "initAdapter: setDefaultConnParam UNKNOWN_COMMAND (ignored): %s\n", adapter.toString());
703 } else {
704 PrintUtil.fprintf_td(System.err, "initAdapter: setDefaultConnParam failed: %s, %s\n", status.toString(), adapter.toString());
705 return false;
706 }
707
708 if( !adapter.setPowered( true ) ) {
709 PrintUtil.fprintf_td(System.err, "initAdapter: setPower.2 on failed: %s\n", adapter.toString());
710 return false;
711 }
712 }
713 PrintUtil.println(System.err, "initAdapter.2: "+adapter.toString());
714
716
717 // adapter is powered-on
718 final AdapterStatusListener asl = new MyAdapterStatusListener();
719 adapter.addStatusListener( asl );
720
721 if( !startAdvertising(adapter, "initAdapter") ) {
722 adapter.removeStatusListener( asl );
723 return false;
724 }
725 return true;
726 }
727
728 private final BTManager.ChangedAdapterSetListener myChangedAdapterSetListener =
730 @Override
731 public void adapterAdded(final BTAdapter adapter) {
732 if( null == chosenAdapter ) {
733 if( initAdapter( adapter ) ) {
734 chosenAdapter = adapter;
735 PrintUtil.println(System.err, "****** Adapter ADDED__: InitOK: " + adapter);
736 } else {
737 PrintUtil.println(System.err, "****** Adapter ADDED__: Ignored: " + adapter);
738 }
739 } else {
740 PrintUtil.println(System.err, "****** Adapter ADDED__: Ignored (other): " + adapter);
741 }
742 }
743
744 @Override
745 public void adapterRemoved(final BTAdapter adapter) {
746 if( null != chosenAdapter && adapter == chosenAdapter ) {
747 chosenAdapter = null;
748 PrintUtil.println(System.err, "****** Adapter REMOVED: " + adapter);
749 } else {
750 PrintUtil.println(System.err, "****** Adapter REMOVED (other): " + adapter);
751 }
752 }
753 };
754
755 public void runTest(final BTManager manager) {
756 timestamp_t0 = Clock.currentTimeMillis();
757
758 PrintUtil.println(System.err, "****** Test Start");
759
760 final MyGATTServerListener listener = new MyGATTServerListener();
761 dbGattServer.addListener( listener );
762
763 manager.addChangedAdapterSetListener(myChangedAdapterSetListener);
764
765 while( !RUN_ONLY_ONCE || 0 == servedConnections ) {
766 try {
767 Thread.sleep(2000);
768 } catch (final InterruptedException e) {
769 e.printStackTrace();
770 }
771 }
772
773 PrintUtil.println(System.err, "****** Shutdown.01 (DBGattServer.remove-listener)");
774 dbGattServer.removeListener( listener );
775
776 PrintUtil.println(System.err, "****** Test Shutdown.02 (listener.close)");
777 listener.close();
778
779 PrintUtil.println(System.err, "****** Test Shutdown.03 (DBGattServer.close)");
780 dbGattServer.close();
781
782 // chosenAdapter = null;
783
784 PrintUtil.println(System.err, "****** Test End");
785 }
786
787 public static void main(final String[] args) throws InterruptedException {
788 for(int i=0; i< args.length; i++) {
789 final String arg = args[i];
790 if( arg.equals("-debug") ) {
791 System.setProperty("org.tinyb.verbose", "true");
792 System.setProperty("org.tinyb.debug", "true");
793 } else if( arg.equals("-verbose") ) {
794 System.setProperty("org.tinyb.verbose", "true");
795 } else if( arg.equals("-dbt_debug") && args.length > (i+1) ) {
796 System.setProperty("direct_bt.debug", args[++i]);
797 } else if( arg.equals("-dbt_verbose") && args.length > (i+1) ) {
798 System.setProperty("direct_bt.verbose", args[++i]);
799 } else if( arg.equals("-dbt_gatt") && args.length > (i+1) ) {
800 System.setProperty("direct_bt.gatt", args[++i]);
801 } else if( arg.equals("-dbt_l2cap") && args.length > (i+1) ) {
802 System.setProperty("direct_bt.l2cap", args[++i]);
803 } else if( arg.equals("-dbt_hci") && args.length > (i+1) ) {
804 System.setProperty("direct_bt.hci", args[++i]);
805 } else if( arg.equals("-dbt_mgmt") && args.length > (i+1) ) {
806 System.setProperty("direct_bt.mgmt", args[++i]);
807 }
808 }
809 final BTManager manager;
810 try {
811 manager = BTFactory.getDirectBTManager();
812 } catch (BTException | NoSuchMethodException | SecurityException
813 | IllegalAccessException | IllegalArgumentException
814 | InvocationTargetException | ClassNotFoundException e) {
815 System.err.println("Unable to instantiate Direct-BT BluetoothManager");
816 e.printStackTrace();
817 System.exit(-1);
818 return;
819 }
820 PrintUtil.println(System.err, "Direct-BT BluetoothManager initialized!");
821 PrintUtil.println(System.err, "Direct-BT Native Version "+BTFactory.getNativeVersion()+" (API "+BTFactory.getNativeAPIVersion()+")");
822 PrintUtil.println(System.err, "Direct-BT Java Version "+BTFactory.getImplVersion()+" (API "+BTFactory.getAPIVersion()+")");
823
824 final DBTPeripheral00 test = new DBTPeripheral00();
825
826 boolean waitForEnter=false;
827 {
828 for(int i=0; i< args.length; i++) {
829 final String arg = args[i];
830
831 if( arg.equals("-wait") ) {
832 waitForEnter = true;
833 } else if( arg.equals("-show_update_events") ) {
834 test.SHOW_UPDATE_EVENTS = true;
835 } else if( arg.equals("-btmode") && args.length > (i+1) ) {
836 test.btMode = BTMode.get(args[++i]);
837 } else if( arg.equals("-use_sc") && args.length > (i+1) ) {
838 test.use_SC = 0 != Integer.valueOf(args[++i]);
839 } else if( arg.equals("-adapter") && args.length > (i+1) ) {
840 test.useAdapter = new EUI48( args[++i] );
841 } else if( arg.equals("-name") && args.length > (i+1) ) {
842 test.adapter_name = args[++i];
843 } else if( arg.equals("-short_name") && args.length > (i+1) ) {
844 test.adapter_short_name = args[++i];
845 } else if( arg.equals("-mtu") && args.length > (i+1) ) {
846 test.dbGattServer.setMaxAttMTU( Integer.valueOf( args[++i] ) );
847 } else if( arg.equals("-seclevel") && args.length > (i+1) ) {
848 final int sec_level_i = Integer.valueOf(args[++i]).intValue();
849 test.adapter_sec_level = BTSecurityLevel.get( (byte)( sec_level_i & 0xff ) );
850 } else if( arg.equals("-iocap") && args.length > (i+1) ) {
851 final int v = Integer.valueOf(args[++i]).intValue();
852 test.adapter_sec_io_cap = SMPIOCapability.get( (byte)( v & 0xff ) );
853 } else if( arg.equals("-once") ) {
854 test.RUN_ONLY_ONCE = true;
855 }
856 }
857 PrintUtil.println(System.err, "Run with '[-btmode LE|BREDR|DUAL] [-use_sc 0|1] "+
858 "[-adapter <adapter_address>] "+
859 "[-name <adapter_name>] "+
860 "[-short_name <adapter_short_name>] "+
861 "[-seclevel <int_sec_level>]* "+
862 "[-iocap <int_iocap>]* "+
863 "[-mtu <max att_mtu>] " +
864 "[-once] "+
865 "[-verbose] [-debug] "+
866 "[-dbt_verbose true|false] "+
867 "[-dbt_debug true|false|adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event] "+
868 "[-dbt_mgmt cmd.timeout=3000,ringsize=64,...] "+
869 "[-dbt_hci cmd.complete.timeout=10000,cmd.status.timeout=3000,ringsize=64,...] "+
870 "[-dbt_gatt cmd.read.timeout=500,cmd.write.timeout=500,cmd.init.timeout=2500,ringsize=128,...] "+
871 "[-dbt_l2cap reader.timeout=10000,restart.count=0,...] "+
872 "[-shutdown <int>]'");
873 }
874
875 PrintUtil.println(System.err, "SHOW_UPDATE_EVENTS "+test.SHOW_UPDATE_EVENTS);
876 PrintUtil.println(System.err, "adapter "+test.useAdapter);
877 PrintUtil.println(System.err, "adapter btmode "+test.btMode.toString());
878 PrintUtil.println(System.err, "adapter SC "+test.use_SC);
879 PrintUtil.fprintf_td(System.err, "name %s (short %s)\n", test.adapter_name, test.adapter_short_name);
880 PrintUtil.println(System.err, "adapter mtu "+test.dbGattServer.getMaxAttMTU());
881 PrintUtil.println(System.err, "adapter sec_level "+test.adapter_sec_level);
882 PrintUtil.println(System.err, "adapter io_cap "+test.adapter_sec_io_cap);
883 PrintUtil.println(System.err, "once "+test.RUN_ONLY_ONCE);
884 PrintUtil.println(System.err, "GattServer "+test.dbGattServer.toString());
885
886 if( waitForEnter ) {
887 PrintUtil.println(System.err, "Press ENTER to continue\n");
888 try{ System.in.read();
889 } catch(final Exception e) { }
890 }
891 test.runTest(manager);
892 }
893}
Author: Sven Gothel sgothel@jausoft.com Copyright (c) 2021 Gothel Software e.K.
static final String SERVER_KEY_PATH
This Java peripheral BTRole::Slave GATT server example uses an event driven workflow.
static void main(final String[] args)
void runTest(final BTManager manager)
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...
Unique Bluetooth EUI48 address and BDAddressType tuple.
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
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 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
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 String toString()
native DBGattValue getValue()
Return a copy of this characteristic's native DBGattValue value.
native short getValueHandle()
Characteristics Value Handle.
native boolean setValue(final byte[] source, final int source_pos, final int source_len, final int dest_pos)
Set this characteristic's native value.
Selected standard GATT descriptor numbers in UUID16 format as defined.
Definition: DBGattDesc.java:48
static final String USER_DESC
Definition: DBGattDesc.java:52
Representing a Gatt Characteristic Descriptor object from the GATT server perspective.
Definition: DBGattDesc.java:41
native String toString()
static DBGattDesc createClientCharConfig()
Return a newly constructed Client Characteristic Configuration with a zero uint16_t value of fixed le...
native DBGattValue getValue()
Return a copy of this characteristic descriptor's native DBGattValue value.
Representing a complete list of Gatt Service objects from the GATT server perspective,...
native String toString()
native void setMaxAttMTU(final int v)
Set maximum server Rx ATT_MTU, defaults to 512+1 limit.
boolean resetGattClientCharConfig(final String service_uuid, final String char_uuid)
native int getMaxAttMTU()
Used maximum server Rx ATT_MTU, defaults to 512+1.
DBGattChar findGattChar(final String service_uuid, final String char_uuid)
synchronized boolean removeListener(final Listener l)
synchronized boolean addListener(final Listener l)
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.
native String toString()
A copy of the native GATT value of DBGattChar or DBGattDesc.
static DBGattValue make(final String name)
Convenience DBGattValue ctor function.
byte[] data()
Returns the actual data of this value.
Bit mask of 'Extended Inquiry Response' (EIR) data fields, indicating a set of related data.
boolean isSet(final DataType bit)
void set(final DataType bit)
Collection of 'Extended Advertising Data' (EAD), 'Advertising Data' (AD) or 'Extended Inquiry Respons...
native void setConnInterval(final short min, final short max)
Set slave connection interval range.
native String toString(final boolean includeServices)
final void addFlag(final GAPFlags.Bit f)
native void setName(final String name)
native void setServicesComplete(final boolean v)
native void addService(final String uuid)
Bit mask of 'Extended Inquiry Response' (EIR) data fields, indicating a set of related data.
Definition: GAPFlags.java:35
Bit mask of GATT Characteristic Properties.
GattCharPropertySet set(final Type bit)
LE Link Layer Feature Set (bitmask)
LE Transport PHY bit values (bitmask)
Definition: LE_PHYs.java:36
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:
Each enum represents a 'Extended Inquiry Response' (EIR) data field type bit.
Each enum represents a 'Extended Inquiry Response' (EIR) data field type bit.
Definition: GAPFlags.java:45
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.
UNSET
Denoting unset value, i.e.
static SMPIOCapability get(final String name)
Maps the specified name to a constant of SMPIOCapability.
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
boolean isInitialized()
Returns true, if initialize(BTMode) has already been called for this adapter, otherwise false.
LE_Features getLEFeatures()
Return LE_Features for this controller.
boolean setPowered(final boolean power_on)
Sets the power state the adapter.
HCIStatusCode startAdvertising(final DBGattServer gattServerData, final EInfoReport eir, final EIRDataTypeSet adv_mask, final EIRDataTypeSet scanrsp_mask, final short adv_interval_min, final short adv_interval_max, final byte adv_type, final byte adv_chan_map, final byte filter_policy)
Starts advertising.
HCIStatusCode setSecureConnections(final boolean enable)
Enable or disable Secure Connections (SC) of the adapter.
String getName()
Returns the name.
void setSMPKeyPath(final String path)
Set the adapter's persistent storage directory for SMPKeyBin files.
void setServerConnSecurity(final BTSecurityLevel sec_level, final SMPIOCapability io_cap)
Sets the given ::BTSecurityLevel and ::SMPIOCapability for connecting device when in server (peripher...
boolean addStatusListener(final AdapterStatusListener listener)
Add the given AdapterStatusListener to the list if not already present.
HCIStatusCode setDefaultConnParam(final short conn_interval_min, final short conn_interval_max, final short conn_latency, final short supervision_timeout)
Set default connection parameter of incoming connections for this adapter when in server mode,...
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.
HCIStatusCode setName(String name, String short_name)
Sets the name and short-name.
boolean removeStatusListener(final AdapterStatusListener l)
Remove the given AdapterStatusListener from the list.
HCIStatusCode stopAdvertising()
Ends advertising.
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.
boolean sendNotification(final short char_value_handle, final byte[] value)
Send a notification event consisting out of the given value representing the given characteristic val...
boolean sendIndication(final short char_value_handle, final byte[] value)
Send an indication event consisting out of the given value representing the given characteristic valu...
boolean remove()
Remove this device from the system (like an unpair).
String getResponderSMPPassKeyString()
Returns getResponderSMPPassKey() as a canonical string, e.g.
boolean getConnected()
Returns the connected state of the device.
boolean equals(final Object obj)
If both types are of BTDevice, it compares their BDAddressAndType, see getAddressAndType().
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...
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.