26import java.lang.reflect.InvocationTargetException;
27import java.util.Iterator;
29import java.util.concurrent.atomic.AtomicInteger;
30import java.util.function.Consumer;
32import org.direct_bt.AdapterSettings;
33import org.direct_bt.AdapterStatusListener;
34import org.direct_bt.BTMode;
35import org.direct_bt.BTSecurityLevel;
36import org.direct_bt.BTAdapter;
37import org.direct_bt.BTDevice;
38import org.direct_bt.BTDeviceRegistry;
39import org.direct_bt.BTException;
40import org.direct_bt.BTFactory;
41import org.direct_bt.BTGattChar;
42import org.direct_bt.BTGattCharListener;
43import org.direct_bt.BTGattCmd;
44import org.direct_bt.BTGattDesc;
45import org.direct_bt.BTGattService;
46import org.direct_bt.BTManager;
47import org.direct_bt.BTSecurityRegistry;
48import org.direct_bt.BTUtils;
49import org.direct_bt.DiscoveryPolicy;
50import org.direct_bt.EIRDataTypeSet;
51import org.direct_bt.EInfoReport;
52import org.direct_bt.GattCharPropertySet;
53import org.direct_bt.HCIStatusCode;
54import org.direct_bt.LE_Features;
55import org.direct_bt.LE_PHYs;
56import org.direct_bt.PairingMode;
57import org.direct_bt.SMPIOCapability;
58import org.direct_bt.SMPKeyBin;
59import org.direct_bt.SMPPairingState;
60import org.direct_bt.ScanType;
61import org.jau.io.PrintUtil;
62import org.jau.net.EUI48;
63import org.jau.sys.Clock;
64import org.jau.util.BasicTypes;
66import jau.direct_bt.DBTManager;
81 EUI48 useAdapter = EUI48.ALL_DEVICE;
85 int RESET_ADAPTER_EACH_CONN = 0;
86 AtomicInteger deviceReadyCount =
new AtomicInteger(0);
88 AtomicInteger MULTI_MEASUREMENTS =
new AtomicInteger(8);
89 boolean KEEP_CONNECTED =
true;
91 boolean GATT_PING_ENABLED =
false;
92 boolean REMOVE_DEVICE =
true;
95 String cmd_uuid =
"d0ca6bf3-3d52-4760-98e5-fc5883e93712";
96 String cmd_rsp_uuid =
"d0ca6bf3-3d53-4760-98e5-fc5883e93712";
97 byte cmd_arg = (byte)0x44;
99 boolean SHOW_UPDATE_EVENTS =
false;
100 boolean QUIET =
false;
102 int shutdownTest = 0;
104 static void executeOffThread(
final Runnable runobj,
final String threadName,
final boolean detach) {
105 final Thread t =
new Thread( runobj, threadName );
111 static void execute(
final Runnable task,
final boolean offThread) {
113 final Thread t =
new Thread(task);
125 final boolean initialSetting = oldmask.
isEmpty();
126 if( initialSetting ) {
127 PrintUtil.println(System.err,
"****** SETTINGS: "+oldmask+
" -> "+newmask+
", initial "+changedmask);
129 PrintUtil.println(System.err,
"****** SETTINGS: "+oldmask+
" -> "+newmask+
", changed "+changedmask);
131 PrintUtil.println(System.err,
"Status Adapter:");
132 PrintUtil.println(System.err, adapter.
toString());
133 if( !initialSetting &&
138 executeOffThread( () -> { startDiscovery(adapter,
"powered-on"); },
145 PrintUtil.println(System.err,
"****** DISCOVERING: meta "+currentMeta+
", changed["+changedType+
", enabled "+changedEnabled+
", policy "+policy+
"] on "+adapter);
149 public boolean deviceFound(
final BTDevice device,
final long timestamp) {
156 PrintUtil.println(System.err,
"****** FOUND__-0: Connecting "+device.
toString());
158 final long td = Clock.currentTimeMillis() - timestamp_t0;
159 PrintUtil.println(System.err,
"PERF: adapter-init -> FOUND__-0 " + td +
" ms");
161 executeOffThread( () -> { connectDiscoveredDevice(device); },
166 PrintUtil.println(System.err,
"****** FOUND__-1: NOP "+device.
toString());
174 if( !QUIET && SHOW_UPDATE_EVENTS ) {
175 PrintUtil.println(System.err,
"****** UPDATED: "+updateMask+
" of "+device);
180 public void deviceConnected(
final BTDevice device,
final boolean discovered,
final long timestamp) {
181 PrintUtil.println(System.err,
"****** CONNECTED (discovered "+discovered+
"): "+device.
toString());
186 PrintUtil.println(System.err,
"****** PAIRING_STATE: state "+state+
", mode "+mode+
": "+device);
196 case REQUESTED_BY_RESPONDER:
199 case FEATURE_EXCHANGE_STARTED:
202 case FEATURE_EXCHANGE_COMPLETED:
205 case PASSKEY_EXPECTED: {
215 case NUMERIC_COMPARE_EXPECTED: {
227 case KEY_DISTRIBUTION:
239 public void deviceReady(
final BTDevice device,
final long timestamp) {
240 deviceReadyCount.incrementAndGet();
241 PrintUtil.println(System.err,
"****** READY-0: Processing["+deviceReadyCount.get()+
"] "+device.
toString());
243 final long td = Clock.currentTimeMillis() - timestamp_t0;
244 PrintUtil.println(System.err,
"PERF: adapter-init -> READY-0 " + td +
" ms");
246 processReadyDevice(device);
250 public void deviceDisconnected(
final BTDevice device,
final HCIStatusCode reason,
final short handle,
final long timestamp) {
251 PrintUtil.println(System.err,
"****** DISCONNECTED: Reason "+reason+
", old handle 0x"+Integer.toHexString(handle)+
": "+device+
" on "+device.
getAdapter());
253 if( REMOVE_DEVICE ) {
254 executeOffThread( () -> { removeDevice(device); },
"DBT-Remove-"+device.
getAddressAndType(),
true );
256 if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount.get() % RESET_ADAPTER_EACH_CONN ) {
257 executeOffThread( () -> { resetAdapter(device.
getAdapter(), 1); },
263 public String toString() {
264 return "AdapterStatusListener[user, per-adapter]";
269 private final int i, j;
271 public MyGATTEventListener(
final int i_,
final int j_) { i=i_; j=j_; }
274 public void notificationReceived(
final BTGattChar charDecl,
275 final byte[] value,
final long timestamp) {
276 final long tR = Clock.currentTimeMillis();
277 PrintUtil.fprintf_td(System.err,
"**[%02d.%02d] Characteristic-Notify: UUID %s, td %d ******\n",
278 i, j, charDecl.
getUUID(), (tR-timestamp));
279 PrintUtil.fprintf_td(System.err,
"**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.
toString());
280 PrintUtil.fprintf_td(System.err,
"**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BasicTypes.bytesHexString(value, 0, -1,
true));
281 PrintUtil.fprintf_td(System.err,
"**[%02d.%02d] Value S: %s ******\n", i, j,
BTUtils.
decodeUTF8String(value, 0, value.length));
287 public void indicationReceived(
final BTGattChar charDecl,
288 final byte[] value,
final long timestamp,
final boolean confirmationSent) {
289 final long tR = Clock.currentTimeMillis();
290 PrintUtil.fprintf_td(System.err,
"**[%02d.%02d] Characteristic-Indication: UUID %s, td %d, confirmed %b ******\n",
291 i, j, charDecl.
getUUID(), (tR-timestamp), confirmationSent);
292 PrintUtil.fprintf_td(System.err,
"**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.
toString());
293 PrintUtil.fprintf_td(System.err,
"**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BasicTypes.bytesHexString(value, 0, -1,
true));
294 PrintUtil.fprintf_td(System.err,
"**[%02d.%02d] Value S: %s ******\n", i, j,
BTUtils.
decodeUTF8String(value, 0, value.length));
300 private void connectDiscoveredDevice(
final BTDevice device) {
301 PrintUtil.println(System.err,
"****** Connecting Device: Start " + device.
toString());
307 if( SHOW_UPDATE_EVENTS ) {
308 PrintUtil.println(System.err,
"****** UPDATED(2): "+updateMask+
" of "+device);
312 public void deviceConnected(
final BTDevice device,
final boolean discovered,
final long timestamp) {
313 PrintUtil.println(System.err,
"****** CONNECTED(2) (discovered "+discovered+
"): "+device.
toString());
317 public String toString() {
323 PrintUtil.println(System.err,
"****** Connecting Device: Found SecurityDetail "+sec.toString()+
" for "+device.
toString());
325 PrintUtil.println(System.err,
"****** Connecting Device: No SecurityDetail for "+device.
toString());
329 PrintUtil.fprintf_td(System.err,
"****** Connecting Device: BTDevice::uploadKeys(...) result %s\n", res.toString());
332 if( sec.isSecurityAutoEnabled() ) {
334 PrintUtil.println(System.err,
"****** Connecting Device: Using SecurityDetail.SEC AUTO "+sec+
" -> set OK "+r);
335 }
else if( sec.isSecLevelOrIOCapSet() ) {
336 final boolean r = device.
setConnSecurity(sec.getSecLevel(), sec.getIOCap());
337 PrintUtil.println(System.err,
"****** Connecting Device: Using SecurityDetail.Level+IOCap "+sec+
" -> set OK "+r);
340 PrintUtil.println(System.err,
"****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY ("+sec+
") -> set OK "+r);
344 PrintUtil.println(System.err,
"****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY -> set OK "+r);
350 PrintUtil.println(System.err,
"EIR-+ "+eir.
toString());
352 short conn_interval_min = (short)8;
353 short conn_interval_max = (short)12;
354 final short conn_latency = (short)0;
356 final short[] minmax =
new short[2];
358 conn_interval_min = minmax[0];
359 conn_interval_max = minmax[1];
362 res = device.
connectLE(le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max, conn_latency, supervision_timeout);
363 PrintUtil.println(System.err,
"****** Connecting Device Command, res "+res+
": End result "+res+
" of " + device.
toString());
366 void shutdownTest() {
367 switch( shutdownTest ) {
378 void shutdownTest01() {
384 void shutdownTest02() {
390 private void processReadyDevice(
final BTDevice device) {
391 PrintUtil.println(System.err,
"****** Processing Ready Device: Start " + device.
toString());
392 final long t1 = Clock.currentTimeMillis();
395 final long t2 = Clock.currentTimeMillis();
397 boolean success =
false;
404 PrintUtil.fprintf_td(System.err,
"****** Set Connected LE PHY: status %s: Tx %s, Rx %s\n",
411 PrintUtil.fprintf_td(System.err,
"****** Got Connected LE PHY: status %s: Tx %s, Rx %s\n",
414 final long t3 = Clock.currentTimeMillis();
421 if(
null == primServices || 0 == primServices.size() ) {
424 throw new RuntimeException(
"Processing Ready Device: getServices() failed " + device.
toString());
426 final long t5 = Clock.currentTimeMillis();
429 final long td01 = t1 - timestamp_t0;
430 final long td05 = t5 - timestamp_t0;
433 final long td12 = t2 - t1;
434 final long td23 = t3 - t2;
435 final long td13 = t3 - t1;
436 final long td35 = t5 - t3;
437 PrintUtil.println(System.err, System.lineSeparator()+System.lineSeparator());
438 PrintUtil.println(System.err,
"PERF: GATT primary-services completed"+System.lineSeparator()+
439 "PERF: adapter-init to discovered " + td00 +
" ms,"+System.lineSeparator()+
440 "PERF: adapter-init to processing-start " + td01 +
" ms,"+System.lineSeparator()+
441 "PERF: adapter-init to gatt-complete " + td05 +
" ms,"+System.lineSeparator()+
442 "PERF: discovered to processing-start " + tdc1 +
" ms,"+System.lineSeparator()+
443 "PERF: discovered to gatt-complete " + tdc5 +
" ms,"+System.lineSeparator()+
444 "PERF: SMPKeyBin + LE_PHY " + td13 +
" ms (SMPKeyBin "+td12+
"ms, LE_PHY "+td23+
"ms), "+System.lineSeparator()+
445 "PERF: get-gatt-services " + td35 +
" ms,"+System.lineSeparator());
448 if(
null != cmd_uuid ) {
449 final BTGattCmd cmd =
null != cmd_rsp_uuid ?
new BTGattCmd(device,
"TestCmd",
null , cmd_uuid, cmd_rsp_uuid)
450 :
new BTGattCmd(device,
"TestCmd",
null , cmd_uuid);
452 final boolean cmd_resolved = cmd.
isResolved();
453 PrintUtil.println(System.err,
"Command test: "+cmd.
toString()+
", resolved "+cmd_resolved);
454 final byte[] cmd_data = { cmd_arg };
459 if( 1 == resp.length && resp[0] == cmd_arg ) {
460 PrintUtil.fprintf_td(System.err,
"Success: %s -> %s (echo response)\n", cmd.
toString(), BasicTypes.bytesHexString(resp, 0, resp.length,
true ));
462 PrintUtil.fprintf_td(System.err,
"Success: %s -> %s (different response)\n", cmd.
toString(), BasicTypes.bytesHexString(resp, 0, resp.length,
true ));
465 PrintUtil.fprintf_td(System.err,
"Success: %s -> no response\n", cmd.
toString());
468 PrintUtil.fprintf_td(System.err,
"Failure: %s -> %s\n", cmd.
toString(), cmd_res.toString());
475 for(
final Iterator<BTGattService> srvIter = primServices.iterator(); srvIter.hasNext(); i++) {
478 PrintUtil.fprintf_td(System.err,
" [%02d] Service UUID %s\n", i, primService.
getUUID());
479 PrintUtil.fprintf_td(System.err,
" [%02d] %s\n", i, primService.
toString());
482 final List<BTGattChar> serviceCharacteristics = primService.
getChars();
483 for(
final Iterator<BTGattChar> charIter = serviceCharacteristics.iterator(); charIter.hasNext(); j++) {
484 final BTGattChar serviceChar = charIter.next();
486 PrintUtil.fprintf_td(System.err,
" [%02d.%02d] Characteristic: UUID %s\n", i, j, serviceChar.
getUUID());
487 PrintUtil.fprintf_td(System.err,
" [%02d.%02d] %s\n", i, j, serviceChar.
toString());
491 final byte[] value = serviceChar.
readValue();
494 PrintUtil.fprintf_td(System.err,
" [%02d.%02d] value: %s ('%s')\n", i, j, BasicTypes.bytesHexString(value, 0, -1,
true), svalue);
498 final List<BTGattDesc> charDescList = serviceChar.
getDescriptors();
499 for(
final Iterator<BTGattDesc> descIter = charDescList.iterator(); descIter.hasNext(); k++) {
502 PrintUtil.fprintf_td(System.err,
" [%02d.%02d.%02d] Descriptor: UUID %s\n", i, j, k, charDesc.
getUUID());
503 PrintUtil.fprintf_td(System.err,
" [%02d.%02d.%02d] %s\n", i, j, k, charDesc.
toString());
506 final boolean cccdEnableResult[] = {
false,
false };
509 final boolean clAdded = serviceChar.
addCharListener(
new MyGATTEventListener(i, j) );
511 PrintUtil.fprintf_td(System.err,
" [%02d.%02d] Characteristic-Listener: Notification(%b), Indication(%b): Added %b\n",
512 i, j, cccdEnableResult[0], cccdEnableResult[1], clAdded);
513 PrintUtil.fprintf_td(System.err,
"\n");
517 PrintUtil.fprintf_td(System.err,
"\n");
519 }
catch(
final Exception ex) {
520 PrintUtil.println(System.err,
"Caught "+ex.getMessage());
521 ex.printStackTrace();
526 }
catch (
final InterruptedException e) {
530 }
catch (
final Throwable t ) {
531 PrintUtil.println(System.err,
"****** Processing Ready Device: Exception caught for " + device.
toString() +
": "+t.getMessage());
535 PrintUtil.println(System.err,
"****** Processing Ready Device: End-1: Success " + success +
" on " + device.
toString());
541 if( KEEP_CONNECTED && GATT_PING_ENABLED && success ) {
543 PrintUtil.println(System.err,
"****** Processing Ready Device: pingGATT OK: "+device.
getAddressAndType());
546 }
catch (
final InterruptedException e) {
550 PrintUtil.println(System.err,
"****** Processing Ready Device: pingGATT failed, waiting for disconnect: "+device.
getAddressAndType());
554 PrintUtil.println(System.err,
"****** Processing Ready Device: End-2: Success " + success +
" on " + device.
toString());
560 if( !KEEP_CONNECTED ) {
564 if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount.get() % RESET_ADAPTER_EACH_CONN ) {
569 if( 0 < MULTI_MEASUREMENTS.get() ) {
570 MULTI_MEASUREMENTS.decrementAndGet();
571 PrintUtil.println(System.err,
"****** Processing Ready Device: MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS.get()+
": "+device.
getAddressAndType());
575 private void removeDevice(
final BTDevice device) {
576 PrintUtil.println(System.err,
"****** Remove Device: removing: "+device.
getAddressAndType());
581 private void resetAdapter(
final BTAdapter adapter,
final int mode) {
582 PrintUtil.println(System.err,
"****** Reset Adapter: reset["+mode+
"] start: "+adapter.
toString());
584 PrintUtil.println(System.err,
"****** Reset Adapter: reset["+mode+
"] end: "+res+
", "+adapter.
toString());
588 boolean le_scan_active =
true;
589 static final short le_scan_interval = (short)24;
590 static final short le_scan_window = (short)24;
591 static final byte filter_policy = (byte)0;
592 static final boolean filter_dup =
true;
594 private boolean startDiscovery(
final BTAdapter adapter,
final String msg) {
596 PrintUtil.fprintf_td(System.err,
"****** Start discovery (%s): Adapter not selected: %s\n", msg, adapter.
toString());
599 final HCIStatusCode status = adapter.
startDiscovery(
null, discoveryPolicy, le_scan_active, le_scan_interval, le_scan_window, filter_policy, filter_dup );
600 PrintUtil.println(System.err,
"****** Start discovery ("+msg+
") result: "+status);
601 return HCIStatusCode.SUCCESS == status;
604 private boolean initAdapter(
final BTAdapter adapter) {
606 PrintUtil.fprintf_td(System.err,
"initAdapter: Adapter not selected: %s\n", adapter.
toString());
613 PrintUtil.fprintf_td(System.err,
"initAdapter: Adapter initialization failed: %s: %s\n",
614 status.toString(), adapter.
toString());
618 PrintUtil.fprintf_td(System.err,
"initAdapter: Already initialized adapter power-on failed:: %s\n", adapter.
toString());
622 PrintUtil.fprintf_td(System.err,
"initAdapter: %s\n", adapter.
toString());
625 PrintUtil.fprintf_td(System.err,
"initAdapter: LE_Features %s\n", le_feats.
toString());
632 PrintUtil.fprintf_td(System.err,
"initAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n",
638 if( !startDiscovery(adapter,
"initAdapter") ) {
645 private final BTManager.ChangedAdapterSetListener myChangedAdapterSetListener =
648 public void adapterAdded(
final BTAdapter adapter) {
649 if(
null == chosenAdapter ) {
650 if( initAdapter( adapter ) ) {
651 chosenAdapter = adapter;
652 PrintUtil.println(System.err,
"****** Adapter ADDED__: InitOK: " + adapter);
654 PrintUtil.println(System.err,
"****** Adapter ADDED__: Ignored: " + adapter);
657 PrintUtil.println(System.err,
"****** Adapter ADDED__: Ignored (other): " + adapter);
662 public void adapterRemoved(
final BTAdapter adapter) {
663 if(
null != chosenAdapter && adapter == chosenAdapter ) {
664 chosenAdapter =
null;
665 PrintUtil.println(System.err,
"****** Adapter REMOVED: " + adapter);
667 PrintUtil.println(System.err,
"****** Adapter REMOVED (other): " + adapter);
673 timestamp_t0 = Clock.currentTimeMillis();
675 boolean done =
false;
680 if( 0 == MULTI_MEASUREMENTS.get() ||
684 PrintUtil.println(System.err,
"****** EOL Test MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS.get()+
692 }
catch (
final InterruptedException e) {
697 chosenAdapter =
null;
702 final List<BTAdapter> adapters = manager.
getAdapters();
704 adapters.forEach(
new Consumer<BTAdapter>() {
707 PrintUtil.println(System.err,
"****** EOL Adapter's Devices - pre_ close: " + a);
711 PrintUtil.println(System.err,
"****** EOL Removed ChangedAdapterSetCallback " + count);
716 adapters.forEach(
new Consumer<BTAdapter>() {
719 PrintUtil.println(System.err,
"****** EOL Adapter's Devices - post close: " + a);
724 public static void main(
final String[] args)
throws InterruptedException {
725 for(
int i=0; i< args.length; i++) {
726 final String arg = args[i];
727 if( arg.equals(
"-debug") ) {
728 System.setProperty(
"org.tinyb.verbose",
"true");
729 System.setProperty(
"org.tinyb.debug",
"true");
730 }
else if( arg.equals(
"-verbose") ) {
731 System.setProperty(
"org.tinyb.verbose",
"true");
732 }
else if( arg.equals(
"-dbt_debug") && args.length > (i+1) ) {
733 System.setProperty(
"direct_bt.debug", args[++i]);
734 }
else if( arg.equals(
"-dbt_verbose") && args.length > (i+1) ) {
735 System.setProperty(
"direct_bt.verbose", args[++i]);
736 }
else if( arg.equals(
"-dbt_gatt") && args.length > (i+1) ) {
737 System.setProperty(
"direct_bt.gatt", args[++i]);
738 }
else if( arg.equals(
"-dbt_l2cap") && args.length > (i+1) ) {
739 System.setProperty(
"direct_bt.l2cap", args[++i]);
740 }
else if( arg.equals(
"-dbt_hci") && args.length > (i+1) ) {
741 System.setProperty(
"direct_bt.hci", args[++i]);
742 }
else if( arg.equals(
"-dbt_mgmt") && args.length > (i+1) ) {
743 System.setProperty(
"direct_bt.mgmt", args[++i]);
749 }
catch (
BTException | NoSuchMethodException | SecurityException
750 | IllegalAccessException | IllegalArgumentException
751 | InvocationTargetException | ClassNotFoundException e) {
752 System.err.println(
"Unable to instantiate DirectBT BluetoothManager");
757 PrintUtil.println(System.err,
"Direct-BT BluetoothManager initialized!");
763 boolean waitForEnter=
false;
765 for(
int i=0; i< args.length; i++) {
766 final String arg = args[i];
768 if( arg.equals(
"-wait") ) {
770 }
else if( arg.equals(
"-show_update_events") ) {
771 test.SHOW_UPDATE_EVENTS =
true;
772 }
else if( arg.equals(
"-quiet") ) {
774 }
else if( arg.equals(
"-shutdown") && args.length > (i+1) ) {
775 test.shutdownTest = Integer.valueOf(args[++i]).intValue();
776 }
else if( arg.equals(
"-discoveryPolicy") && args.length > (i+1) ) {
777 test.discoveryPolicy =
DiscoveryPolicy.
get((
byte) Integer.valueOf(args[++i]).intValue() );
778 }
else if( arg.equals(
"-scanPassive") ) {
779 test.le_scan_active =
false;
780 }
else if( arg.equals(
"-btmode") && args.length > (i+1) ) {
782 }
else if( arg.equals(
"-adapter") && args.length > (i+1) ) {
783 test.useAdapter =
new EUI48( args[++i] );
784 }
else if( arg.equals(
"-dev") && args.length > (i+1) ) {
786 }
else if( arg.equals(
"-passkey") && args.length > (i+2) ) {
787 final String addrOrNameSub = args[++i];
789 sec.passkey = Integer.valueOf(args[++i]).intValue();
790 System.err.println(
"Set passkey in "+sec);
791 }
else if( arg.equals(
"-seclevel") && args.length > (i+2) ) {
792 final String addrOrNameSub = args[++i];
794 final int sec_level_i = Integer.valueOf(args[++i]).intValue();
796 System.err.println(
"Set sec_level "+sec_level_i+
" in "+sec);
797 }
else if( arg.equals(
"-iocap") && args.length > (i+2) ) {
798 final String addrOrNameSub = args[++i];
800 final int io_cap_i = Integer.valueOf(args[++i]).intValue();
802 System.err.println(
"Set io_cap "+io_cap_i+
" in "+sec);
803 }
else if( arg.equals(
"-secauto") && args.length > (i+2) ) {
804 final String addrOrNameSub = args[++i];
806 final int io_cap_i = Integer.valueOf(args[++i]).intValue();
808 System.err.println(
"Set SEC AUTO security io_cap "+io_cap_i+
" in "+sec);
809 }
else if( arg.equals(
"-cmd") && args.length > (i+1) ) {
810 test.cmd_uuid = args[++i];
811 }
else if( arg.equals(
"-cmdrsp") && args.length > (i+1) ) {
812 test.cmd_rsp_uuid = args[++i];
813 }
else if( arg.equals(
"-cmdarg") && args.length > (i+1) ) {
814 test.cmd_arg = (byte)Integer.valueOf(args[++i]).intValue();
815 }
else if( arg.equals(
"-disconnect") ) {
816 test.KEEP_CONNECTED =
false;
817 }
else if( arg.equals(
"-enableGATTPing") ) {
818 test.GATT_PING_ENABLED =
true;
819 }
else if( arg.equals(
"-keepDevice") ) {
820 test.REMOVE_DEVICE =
false;
821 }
else if( arg.equals(
"-count") && args.length > (i+1) ) {
822 test.MULTI_MEASUREMENTS.set(Integer.valueOf(args[++i]).intValue());
823 }
else if( arg.equals(
"-single") ) {
824 test.MULTI_MEASUREMENTS.set(-1);
825 }
else if( arg.equals(
"-resetEachCon") && args.length > (i+1) ) {
826 test.RESET_ADAPTER_EACH_CONN = Integer.valueOf(args[++i]).intValue();
829 PrintUtil.println(System.err,
"Run with '[-btmode LE|BREDR|DUAL] "+
830 "[-disconnect] [-enableGATTPing] [-count <number>] [-single] [-show_update_events] [-quiet] "+
831 "[-discoveryPolicy <0-4>] "+
833 "[-resetEachCon connectionCount] "+
834 "[-adapter <adapter_address>] "+
835 "(-dev <device_[address|name]_sub>)* "+
836 "(-seclevel <device_[address|name]_sub> <int_sec_level>)* "+
837 "(-iocap <device_[address|name]_sub> <int_iocap>)* "+
838 "(-secauto <device_[address|name]_sub> <int_iocap>)* "+
839 "(-passkey <device_[address|name]_sub> <digits>)* "+
840 "[-cmd <uuid>] [-cmdrsp <uuid>] [-cmdarg <byte-val>] "+
841 "[-verbose] [-debug] "+
842 "[-dbt_verbose true|false] "+
843 "[-dbt_debug true|false|adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event] "+
844 "[-dbt_mgmt cmd.timeout=3000,ringsize=64,...] "+
845 "[-dbt_hci cmd.complete.timeout=10000,cmd.status.timeout=3000,ringsize=64,...] "+
846 "[-dbt_gatt cmd.read.timeout=500,cmd.write.timeout=500,cmd.init.timeout=2500,ringsize=128,...] "+
847 "[-dbt_l2cap reader.timeout=10000,restart.count=0,...] "+
848 "[-shutdown <int>]'");
851 PrintUtil.println(System.err,
"MULTI_MEASUREMENTS "+test.MULTI_MEASUREMENTS.get());
852 PrintUtil.println(System.err,
"KEEP_CONNECTED "+test.KEEP_CONNECTED);
853 PrintUtil.println(System.err,
"RESET_ADAPTER_EACH_CONN "+test.RESET_ADAPTER_EACH_CONN);
854 PrintUtil.println(System.err,
"GATT_PING_ENABLED "+test.GATT_PING_ENABLED);
855 PrintUtil.println(System.err,
"REMOVE_DEVICE "+test.REMOVE_DEVICE);
856 PrintUtil.println(System.err,
"SHOW_UPDATE_EVENTS "+test.SHOW_UPDATE_EVENTS);
857 PrintUtil.println(System.err,
"QUIET "+test.QUIET);
858 PrintUtil.println(System.err,
"adapter "+test.useAdapter);
859 PrintUtil.println(System.err,
"btmode "+test.btMode.toString());
860 PrintUtil.println(System.err,
"discoveryPolicy "+test.discoveryPolicy.toString());
861 PrintUtil.println(System.err,
"le_scan_active "+test.le_scan_active);
862 PrintUtil.println(System.err,
"Command: cmd "+test.cmd_uuid+
", arg 0x"+Integer.toHexString(test.cmd_arg));
863 PrintUtil.println(System.err,
" rsp "+test.cmd_rsp_uuid);
868 PrintUtil.println(System.err,
"Press ENTER to continue\n");
869 try{ System.in.read();
870 }
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 scanner BTRole::Master GATT client example uses an event driven workflow and multithreading...
static void main(final String[] args)
void runTest(final BTManager manager)
final void shutdown()
Release the native memory associated with this object and all related Bluetooth resources.
static final BTManager getManager()
Returns an instance of BluetoothManager, to be used instead of constructor.
Author: Sven Gothel sgothel@jausoft.com Copyright (c) 2020 Gothel Software e.K.
boolean isSet(final SettingType bit)
BTAdapter status listener for remote BTDevice discovery events: Added, updated and removed; as well a...
Application toolkit providing BT device registration of processed and awaited devices.
static 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,...
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...
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.
static BTSecurityLevel get(final String name)
Maps the specified name to a constant of BTSecurityLevel.
Discovery policy defines the BTAdapter discovery mode after connecting a remote BTDevice:
PAUSE_CONNECTED_UNTIL_DISCONNECTED
Pause discovery until all connected BTDevice become disconnected, effectively until AdapterStatusList...
static DiscoveryPolicy get(final byte value)
Maps the specified integer value to a constant of DiscoveryPolicy.
PAUSE_CONNECTED_UNTIL_READY
Pause discovery until all connected BTDevice reach readiness inclusive optional SMP pairing (~120ms) ...
Each enum represents a 'Extended Inquiry Response' (EIR) data field type bit.
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1.1 Characteristic Properties.
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
Each enum represents a 'LE Transport PHY' bit value.
Bluetooth secure pairing mode.
static SMPIOCapability get(final String name)
Maps the specified name to a constant of SMPIOCapability.
KEYBOARD_ONLY
Keyboard input only, value 2.
SMP Pairing Process state definition.
Meta ScanType as derived from BTMode with defined value mask consisting of BDAddressType bits.
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.
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.