26import java.lang.reflect.InvocationTargetException;
27import java.nio.charset.StandardCharsets;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Iterator;
32import java.util.concurrent.atomic.AtomicInteger;
33import java.util.function.Consumer;
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;
74import jau.direct_bt.DBTManager;
94 EUI48 useAdapter = EUI48.ALL_DEVICE;
98 int RESET_ADAPTER_EACH_CONN = 0;
99 AtomicInteger deviceReadyCount =
new AtomicInteger(0);
101 AtomicInteger MULTI_MEASUREMENTS =
new AtomicInteger(8);
102 boolean KEEP_CONNECTED =
true;
104 boolean GATT_PING_ENABLED =
false;
105 boolean REMOVE_DEVICE =
true;
108 static String cmd_req_uuid =
"2c1b2472-4a5f-11e5-9595-0002a5d5c51b";
109 static String cmd_rsp_uuid =
"2c1b2473-4a5f-11e5-9595-0002a5d5c51b";
111 static final byte[] cmd_data1 = { 0x00 ,
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 };
116 static final byte[] cmd_data2 = { 0x01 ,
119 static final byte[] resp_exp = { 0x00 ,
120 0x14, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00,
121 0x01, (byte)0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 (
byte)0xf6, 0x64, 0x17,
125 boolean SHOW_UPDATE_EVENTS =
false;
126 boolean QUIET =
false;
128 int shutdownTest = 0;
130 static void executeOffThread(
final Runnable runobj,
final String threadName,
final boolean detach) {
131 final Thread t =
new Thread( runobj, threadName );
137 static void execute(
final Runnable task,
final boolean offThread) {
139 final Thread t =
new Thread(task);
155 new ArrayList<DBGattDesc>( ),
159 new ArrayList<DBGattDesc>( ),
167 new ArrayList<DBGattDesc>( ),
171 new ArrayList<DBGattDesc>( ),
175 new ArrayList<DBGattDesc>( ),
179 new ArrayList<DBGattDesc>( ),
183 new ArrayList<DBGattDesc>( ),
187 new ArrayList<DBGattDesc>( ),
196 final boolean initialSetting = oldmask.
isEmpty();
197 if( initialSetting ) {
198 PrintUtil.println(System.err,
"****** SETTINGS: "+oldmask+
" -> "+newmask+
", initial "+changedmask);
200 PrintUtil.println(System.err,
"****** SETTINGS: "+oldmask+
" -> "+newmask+
", changed "+changedmask);
202 PrintUtil.println(System.err,
"Status Adapter:");
203 PrintUtil.println(System.err, adapter.
toString());
204 if( !initialSetting &&
209 executeOffThread( () -> { startDiscovery(adapter,
"powered-on"); },
216 PrintUtil.println(System.err,
"****** DISCOVERING: meta "+currentMeta+
", changed["+changedType+
", enabled "+changedEnabled+
", policy "+policy+
"] on "+adapter);
220 public boolean deviceFound(
final BTDevice device,
final long timestamp) {
227 PrintUtil.println(System.err,
"****** FOUND__-0: Connecting "+device.
toString());
229 final long td = Clock.currentTimeMillis() - timestamp_t0;
230 PrintUtil.println(System.err,
"PERF: adapter-init -> FOUND__-0 " + td +
" ms");
232 executeOffThread( () -> { connectDiscoveredDevice(device); },
237 PrintUtil.println(System.err,
"****** FOUND__-1: NOP "+device.
toString());
245 if( !QUIET && SHOW_UPDATE_EVENTS ) {
246 PrintUtil.println(System.err,
"****** UPDATED: "+updateMask+
" of "+device);
251 public void deviceConnected(
final BTDevice device,
final boolean discovered,
final long timestamp) {
252 PrintUtil.println(System.err,
"****** CONNECTED (discovered "+discovered+
"): "+device.
toString());
257 PrintUtil.println(System.err,
"****** PAIRING_STATE: state "+state+
", mode "+mode+
": "+device);
267 case REQUESTED_BY_RESPONDER:
270 case FEATURE_EXCHANGE_STARTED:
273 case FEATURE_EXCHANGE_COMPLETED:
276 case PASSKEY_EXPECTED: {
286 case NUMERIC_COMPARE_EXPECTED: {
298 case KEY_DISTRIBUTION:
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());
314 final long td = Clock.currentTimeMillis() - timestamp_t0;
315 PrintUtil.println(System.err,
"PERF: adapter-init -> READY-0 " + td +
" ms");
317 processReadyDevice(device);
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());
324 if( REMOVE_DEVICE ) {
325 executeOffThread( () -> { removeDevice(device); },
"DBT-Remove-"+device.
getAddressAndType(),
true );
327 if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount.get() % RESET_ADAPTER_EACH_CONN ) {
328 executeOffThread( () -> { resetAdapter(device.
getAdapter(), 1); },
334 public String toString() {
335 return "AdapterStatusListener[user, per-adapter]";
340 private final int i, j;
342 public MyGATTEventListener(
final int i_,
final int j_) { i=i_; j=j_; }
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));
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));
371 private void connectDiscoveredDevice(
final BTDevice device) {
372 PrintUtil.println(System.err,
"****** Connecting Device: Start " + device.
toString());
378 if( SHOW_UPDATE_EVENTS ) {
379 PrintUtil.println(System.err,
"****** UPDATED(2): "+updateMask+
" of "+device);
383 public void deviceConnected(
final BTDevice device,
final boolean discovered,
final long timestamp) {
384 PrintUtil.println(System.err,
"****** CONNECTED(2) (discovered "+discovered+
"): "+device.
toString());
388 public String toString() {
394 PrintUtil.println(System.err,
"****** Connecting Device: Found SecurityDetail "+sec.toString()+
" for "+device.
toString());
396 PrintUtil.println(System.err,
"****** Connecting Device: No SecurityDetail for "+device.
toString());
400 PrintUtil.fprintf_td(System.err,
"****** Connecting Device: BTDevice::uploadKeys(...) result %s\n", res.toString());
403 if( sec.isSecurityAutoEnabled() ) {
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);
411 PrintUtil.println(System.err,
"****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY ("+sec+
") -> set OK "+r);
415 PrintUtil.println(System.err,
"****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY -> set OK "+r);
421 PrintUtil.println(System.err,
"EIR-+ "+eir.
toString());
423 short conn_interval_min = (short)8;
424 short conn_interval_max = (short)12;
425 final short conn_latency = (short)0;
427 final short[] minmax =
new short[2];
429 conn_interval_min = minmax[0];
430 conn_interval_max = minmax[1];
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());
437 void shutdownTest() {
438 switch( shutdownTest ) {
449 void shutdownTest01() {
455 void shutdownTest02() {
461 private void processReadyDevice(
final BTDevice device) {
462 PrintUtil.println(System.err,
"****** Processing Ready Device: Start " + device.
toString());
463 final long t1 = Clock.currentTimeMillis();
466 final long t2 = Clock.currentTimeMillis();
468 boolean success =
false;
475 PrintUtil.fprintf_td(System.err,
"****** Set Connected LE PHY: status %s: Tx %s, Rx %s\n",
482 PrintUtil.fprintf_td(System.err,
"****** Got Connected LE PHY: status %s: Tx %s, Rx %s\n",
485 final long t3 = Clock.currentTimeMillis();
492 if(
null == primServices || 0 == primServices.size() ) {
495 throw new RuntimeException(
"Processing Ready Device: getServices() failed " + device.
toString());
497 final long t5 = Clock.currentTimeMillis();
500 final long td01 = t1 - timestamp_t0;
501 final long td05 = t5 - timestamp_t0;
504 final long td12 = t2 - t1;
505 final long td23 = t3 - t2;
506 final long td13 = t3 - t1;
507 final long td35 = t5 - t3;
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());
520 final int response_size = resp_exp.length;
522 final BTGattCmd cmd =
new BTGattCmd(device,
"TestCmd",
null , cmd_req_uuid, cmd_rsp_uuid);
530 PrintUtil.fprintf_td(System.err,
"Received: size %d, %s\n", value.length, BasicTypes.bytesHexString(value, 0, -1,
true));
532 PrintUtil.println(System.err,
"Command test: "+cmd.
toString());
533 cmd_res = cmd.
sendOnly(
true , cmd_data1);
538 cmd_res = cmd.
send(
true , cmd_data2, 3000 );
547 if( response_size != resp.length ) {
549 PrintUtil.fprintf_td(System.err,
"Success: %s -> %s (response size)\n", cmd.
toString(), BasicTypes.bytesHexString(resp, 0, resp.length,
true ));
550 }
else if( !Arrays.equals(resp_exp, resp) ) {
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 ));
554 PrintUtil.fprintf_td(System.err,
"- has %s\n", BasicTypes.bytesHexString(resp, 0, resp.length,
true ));
556 PrintUtil.fprintf_td(System.err,
"Success: %s -> %s\n", cmd.
toString(), BasicTypes.bytesHexString(resp, 0, resp.length,
true ));
560 if( 0 != fail_point ) {
561 PrintUtil.fprintf_td(System.err,
"Failure: point %d: %s -> %s\n", fail_point, cmd.
toString(), cmd_res.toString());
568 for(
final Iterator<BTGattService> srvIter = primServices.iterator(); srvIter.hasNext(); i++) {
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());
575 final List<BTGattChar> serviceCharacteristics = primService.
getChars();
576 for(
final Iterator<BTGattChar> charIter = serviceCharacteristics.iterator(); charIter.hasNext(); j++) {
577 final BTGattChar serviceChar = charIter.next();
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());
584 final byte[] value = serviceChar.
readValue();
587 PrintUtil.fprintf_td(System.err,
" [%02d.%02d] value: %s ('%s')\n", i, j, BasicTypes.bytesHexString(value, 0, -1,
true), svalue);
591 final List<BTGattDesc> charDescList = serviceChar.
getDescriptors();
592 for(
final Iterator<BTGattDesc> descIter = charDescList.iterator(); descIter.hasNext(); k++) {
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());
599 final boolean cccdEnableResult[] = {
false,
false };
602 final boolean clAdded = serviceChar.
addCharListener(
new MyGATTEventListener(i, j) );
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");
610 PrintUtil.fprintf_td(System.err,
"\n");
612 }
catch(
final Exception ex) {
613 PrintUtil.println(System.err,
"Caught "+ex.getMessage());
614 ex.printStackTrace();
619 }
catch (
final InterruptedException e) {
623 }
catch (
final Throwable t ) {
624 PrintUtil.println(System.err,
"****** Processing Ready Device: Exception caught for " + device.
toString() +
": "+t.getMessage());
628 PrintUtil.println(System.err,
"****** Processing Ready Device: End-1: Success " + success +
" on " + device.
toString());
634 if( KEEP_CONNECTED && GATT_PING_ENABLED && success ) {
636 PrintUtil.println(System.err,
"****** Processing Ready Device: pingGATT OK: "+device.
getAddressAndType());
639 }
catch (
final InterruptedException e) {
643 PrintUtil.println(System.err,
"****** Processing Ready Device: pingGATT failed, waiting for disconnect: "+device.
getAddressAndType());
647 PrintUtil.println(System.err,
"****** Processing Ready Device: End-2: Success " + success +
" on " + device.
toString());
653 if( !KEEP_CONNECTED ) {
657 if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount.get() % RESET_ADAPTER_EACH_CONN ) {
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());
668 private void removeDevice(
final BTDevice device) {
669 PrintUtil.println(System.err,
"****** Remove Device: removing: "+device.
getAddressAndType());
674 private void resetAdapter(
final BTAdapter adapter,
final int mode) {
675 PrintUtil.println(System.err,
"****** Reset Adapter: reset["+mode+
"] start: "+adapter.
toString());
677 PrintUtil.println(System.err,
"****** Reset Adapter: reset["+mode+
"] end: "+res+
", "+adapter.
toString());
681 boolean le_scan_active =
true;
682 static final short le_scan_interval = (short)24;
683 static final short le_scan_window = (short)24;
684 static final byte filter_policy = (byte)0;
685 static final boolean filter_dup =
true;
687 @SuppressWarnings(
"unused")
688 private boolean startDiscovery(
final BTAdapter adapter,
final String msg) {
690 PrintUtil.fprintf_td(System.err,
"****** Start discovery (%s): Adapter not selected: %s\n", msg, adapter.
toString());
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);
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;
708 private boolean initAdapter(
final BTAdapter adapter) {
710 PrintUtil.fprintf_td(System.err,
"initAdapter: Adapter not selected: %s\n", adapter.
toString());
717 PrintUtil.fprintf_td(System.err,
"initAdapter: Adapter initialization failed: %s: %s\n",
718 status.toString(), adapter.
toString());
722 PrintUtil.fprintf_td(System.err,
"initAdapter: Already initialized adapter power-on failed:: %s\n", adapter.
toString());
726 PrintUtil.fprintf_td(System.err,
"initAdapter: %s\n", adapter.
toString());
729 PrintUtil.fprintf_td(System.err,
"initAdapter: LE_Features %s\n", le_feats.
toString());
736 PrintUtil.fprintf_td(System.err,
"initAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n",
742 if( !startDiscovery(adapter,
"initAdapter") ) {
749 private final BTManager.ChangedAdapterSetListener myChangedAdapterSetListener =
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);
758 PrintUtil.println(System.err,
"****** Adapter ADDED__: Ignored: " + adapter);
761 PrintUtil.println(System.err,
"****** Adapter ADDED__: Ignored (other): " + adapter);
766 public void adapterRemoved(
final BTAdapter adapter) {
767 if(
null != chosenAdapter && adapter == chosenAdapter ) {
768 chosenAdapter =
null;
769 PrintUtil.println(System.err,
"****** Adapter REMOVED: " + adapter);
771 PrintUtil.println(System.err,
"****** Adapter REMOVED (other): " + adapter);
777 timestamp_t0 = Clock.currentTimeMillis();
779 boolean done =
false;
784 if( 0 == MULTI_MEASUREMENTS.get() ||
788 PrintUtil.println(System.err,
"****** EOL Test MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS.get()+
796 }
catch (
final InterruptedException e) {
801 chosenAdapter =
null;
806 final List<BTAdapter> adapters = manager.
getAdapters();
808 adapters.forEach(
new Consumer<BTAdapter>() {
811 PrintUtil.println(System.err,
"****** EOL Adapter's Devices - pre_ close: " + a);
815 PrintUtil.println(System.err,
"****** EOL Removed ChangedAdapterSetCallback " + count);
820 adapters.forEach(
new Consumer<BTAdapter>() {
823 PrintUtil.println(System.err,
"****** EOL Adapter's Devices - post close: " + a);
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]);
853 }
catch (
BTException | NoSuchMethodException | SecurityException
854 | IllegalAccessException | IllegalArgumentException
855 | InvocationTargetException | ClassNotFoundException e) {
856 System.err.println(
"Unable to instantiate DirectBT BluetoothManager");
861 PrintUtil.println(System.err,
"Direct-BT BluetoothManager initialized!");
868 final String dev_name_prefix =
"LabPad";
876 boolean waitForEnter=
false;
878 for(
int i=0; i< args.length; i++) {
879 final String arg = args[i];
881 if( arg.equals(
"-wait") ) {
883 }
else if( arg.equals(
"-show_update_events") ) {
884 test.SHOW_UPDATE_EVENTS =
true;
885 }
else if( arg.equals(
"-quiet") ) {
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) ) {
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) ) {
899 sec.passkey = Integer.valueOf(args[++i]).intValue();
900 System.err.println(
"Set passkey in "+sec);
903 PrintUtil.println(System.err,
"Run with '[-btmode LE|BREDR|DUAL] "+
904 "[-disconnect] [-show_update_events] [-quiet] "+
905 "[-discoveryPolicy <0-4>] "+
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,...] ");
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);
933 PrintUtil.println(System.err,
"Press ENTER to continue\n");
934 try{ System.in.read();
935 }
catch(
final Exception e) { }
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 int getWaitForDevicesCount()
static int getProcessedDeviceCount()
static void addToProcessedDevices(final BDAddressAndType a, final String n)
static String getWaitForDevicesString()
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 boolean isWaitingForAnyDevice()
static void addToWaitForDevices(final String addrOrNameSub)
static String getProcessedDevicesString()
One stop BTManager API entry point.
static native String getNativeVersion()
static final String getImplVersion()
Manifest's Attributes.Name#IMPLEMENTATION_VERSION or null if not available.
static final String getAPIVersion()
Manifest's Attributes.Name#SPECIFICATION_VERSION or null if not available.
static native String getNativeAPIVersion()
static synchronized BTManager getDirectBTManager()
Returns an initialized BluetoothManager instance using the DirectBT implementation.
BTGattChar event listener for notification and indication events.
Class maps a GATT command and optionally its asynchronous response to a synchronous atomic operation.
void setVerbose(final boolean v)
Set verbosity for UUID resolution.
synchronized HCIStatusCode send(final boolean prefNoAck, final byte[] cmd_data, final int timeoutMS)
Send the command to the remote BTDevice.
void close()
Close this command instance, usually called at destruction.
byte[] getResponse()
Returns the read-only response data object for configured commands with response notification or indi...
synchronized boolean isResolved()
Query whether all UUIDs of this commands have been resolved.
boolean hasResponseSet()
Return true if a notification or indication response has been set via constructor,...
synchronized HCIStatusCode sendOnly(final boolean prefNoAck, final byte[] cmd_data)
Send the command to the remote BTDevice, only.
void setResponseMinSize(final int v)
void setDataCallback(final DataCallback dcb)
Application toolkit providing BT security setup and its device association on a pattern matching basi...
static final int NO_PASSKEY
static String allToString()
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.
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...
Selected standard GATT characteristic numbers in UUID16 format as defined.
static String SOFTWARE_REVISION_STRING
static String SERIAL_NUMBER_STRING
static String MANUFACTURER_NAME_STRING
static String MODEL_NUMBER_STRING
static String FIRMWARE_REVISION_STRING
static String HARDWARE_REVISION_STRING
static String DEVICE_NAME
Representing a Gatt Characteristic object from the GATT server perspective.
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.
boolean isSet(final Type bit)
LE Link Layer Feature Set (bitmask)
LE Transport PHY bit values (bitmask)
Storage for SMP keys including required connection parameter per local adapter and remote device.
final static boolean remove(final String path, final BTDevice remoteDevice_)
final static String getFilename(final String path, final BDAddressAndType localAddress_, final BDAddressAndType remoteAddress_)
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,...
Bits representing 'BTAdapter setting' data fields.
Bluetooth adapter operating mode.
DUAL
Dual Bluetooth mode, i.e.
static BTMode get(final String name)
Maps the specified name to a constant of BTMode.
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.
Bluetooth secure pairing mode.
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.
BTAdapter represents one local Bluetooth Controller.
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.
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.
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.
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.
A thread safe singleton handler of the BTAdapter manager, e.g.
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.