From 4cbd5badeeca71ed0814b61feb80026838eb4240 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Fri, 25 Sep 2020 09:45:49 +0200 Subject: Java Example: Rename ScannerTinyB10 -> DBTScanner10: Requiring Direct-BT, name aligned with dbt_scanner10.cpp --- README.md | 4 +- examples/java/CMakeLists.txt | 8 +- examples/java/DBTScanner10.java | 704 +++++++++++++++++++++++++++++++++++ examples/java/ScannerTinyB10.java | 704 ----------------------------------- java/org/tinyb/BluetoothFactory.java | 2 +- scripts/run-java-scanner10.sh | 4 +- 6 files changed, 713 insertions(+), 713 deletions(-) create mode 100644 examples/java/DBTScanner10.java delete mode 100644 examples/java/ScannerTinyB10.java diff --git a/README.md b/README.md index ebe80ff6..530c18ed 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ are available, [dbt_scanner10.cpp](https://jausoft.com/projects/direct_bt/build/ demonstrates the event driven and multithreading workflow. *Direct-BT* [Java examples](https://jausoft.com/projects/direct_bt/build/documentation/java/html/examples.html) -are availble, [ScannerTinyB10.java](https://jausoft.com/projects/direct_bt/build/documentation/java/html/ScannerTinyB10_8java-example.html) +are availble, [DBTScanner10.java](https://jausoft.com/projects/direct_bt/build/documentation/java/html/DBTScanner10_8java-example.html) demonstrates the event driven and multithreading workflow - matching *dbt_scanner10.cpp*. A guide for getting started with *TinyB* on Java is available from Intel: @@ -263,7 +263,7 @@ make doc Changes ============ -**2.1.26 Early *Direct-BT* Maturity (Bluetooth LE)** +**2.1.29 Early *Direct-BT* Maturity (Bluetooth LE)** * Reaching robust implementation state of *Direct-BT*, including recovery from L2CAP transmission breakdown on Raspberry Pi. * Resolved race conditions on rapid device discovery and connect, using one thread per device. diff --git a/examples/java/CMakeLists.txt b/examples/java/CMakeLists.txt index 25e719a8..d7a374aa 100644 --- a/examples/java/CMakeLists.txt +++ b/examples/java/CMakeLists.txt @@ -44,10 +44,10 @@ add_custom_command(TARGET ScannerTinyB02 ) add_dependencies(ScannerTinyB02 tinybjar) -add_jar(ScannerTinyB10 SOURCES ScannerTinyB10.java INCLUDE_JARS "${CMAKE_CURRENT_BINARY_DIR}/../../java/tinyb2.jar" ENTRY_POINT Notification) -add_custom_command(TARGET ScannerTinyB10 +add_jar(DBTScanner10 SOURCES DBTScanner10.java INCLUDE_JARS "${CMAKE_CURRENT_BINARY_DIR}/../../java/tinyb2.jar" ENTRY_POINT Notification) +add_custom_command(TARGET DBTScanner10 POST_BUILD - COMMAND cp "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/ScannerTinyB10.dir/ScannerTinyB10.class" "${CMAKE_CURRENT_BINARY_DIR}" + COMMAND cp "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/DBTScanner10.dir/DBTScanner10.class" "${CMAKE_CURRENT_BINARY_DIR}" ) -add_dependencies(ScannerTinyB10 tinybjar) +add_dependencies(DBTScanner10 tinybjar) diff --git a/examples/java/DBTScanner10.java b/examples/java/DBTScanner10.java new file mode 100644 index 00000000..71528ad1 --- /dev/null +++ b/examples/java/DBTScanner10.java @@ -0,0 +1,704 @@ +/** + * Author: Sven Gothel + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.tinyb.AdapterSettings; +import org.tinyb.BluetoothAdapter; +import org.tinyb.BluetoothAddressType; +import org.tinyb.BluetoothDevice; +import org.tinyb.AdapterStatusListener; +import org.tinyb.BLERandomAddressType; +import org.tinyb.BTMode; +import org.tinyb.BluetoothException; +import org.tinyb.BluetoothFactory; +import org.tinyb.BluetoothGattCharacteristic; +import org.tinyb.BluetoothGattDescriptor; +import org.tinyb.BluetoothGattService; +import org.tinyb.BluetoothManager; +import org.tinyb.BluetoothNotification; +import org.tinyb.BluetoothType; +import org.tinyb.BluetoothUtils; +import org.tinyb.EIRDataTypeSet; +import org.tinyb.GATTCharacteristicListener; +import org.tinyb.HCIStatusCode; +import org.tinyb.HCIWhitelistConnectType; +import org.tinyb.PairingMode; + +import direct_bt.tinyb.DBTManager; + +/** + * This Java scanner example uses the Direct-BT fully event driven workflow + * and adds multithreading, i.e. one thread processes each found device found + * as notified via the event listener. + *

+ * This example represents the recommended utilization of Direct-BT. + *

+ */ +public class DBTScanner10 { + static final String EUI48_ANY_DEVICE = "00:00:00:00:00:00"; + + final List waitForDevices = new ArrayList(); + + long timestamp_t0; + + int MULTI_MEASUREMENTS = 8; + boolean KEEP_CONNECTED = true; + boolean GATT_PING_ENABLED = false; + boolean REMOVE_DEVICE = true; + boolean USE_WHITELIST = false; + final List whitelist = new ArrayList(); + final List charIdentifierList = new ArrayList(); + final List charValueList = new ArrayList(); + + boolean SHOW_UPDATE_EVENTS = false; + boolean QUIET = false; + + int dev_id = -1; // use default + + int shutdownTest = 0; + + static void printf(final String format, final Object... args) { + final Object[] args2 = new Object[args.length+1]; + args2[0] = BluetoothUtils.getElapsedMillisecond(); + System.arraycopy(args, 0, args2, 1, args.length); + System.err.printf("[%,9d] "+format, args2); + // System.err.printf("[%,9d] ", BluetoothUtils.getElapsedMillisecond()); + // System.err.printf(format, args); + } + static void println(final String msg) { + System.err.printf("[%,9d] %s%s", BluetoothUtils.getElapsedMillisecond(), msg, System.lineSeparator()); + } + + + Collection devicesInProcessing = Collections.synchronizedCollection(new ArrayList<>()); + Collection devicesProcessed = Collections.synchronizedCollection(new ArrayList<>()); + + final AdapterStatusListener statusListener = new AdapterStatusListener() { + @Override + public void adapterSettingsChanged(final BluetoothAdapter adapter, final AdapterSettings oldmask, + final AdapterSettings newmask, final AdapterSettings changedmask, final long timestamp) { + println("****** SETTINGS: "+oldmask+" -> "+newmask+", changed "+changedmask); + println("Status Adapter:"); + println(adapter.toString()); + if( changedmask.isSet(AdapterSettings.SettingType.POWERED) && + newmask.isSet(AdapterSettings.SettingType.POWERED) ) + { + // powered on adapter .... + if( !adapter.startDiscovery( true ) ) { + println("Adapter (powered-on): Start discovery failed"); + } + } + } + + @Override + public void discoveringChanged(final BluetoothAdapter adapter, final boolean enabled, final boolean keepAlive, final long timestamp) { + println("****** DISCOVERING: enabled "+enabled+", keepAlive "+keepAlive+" on "+adapter); + } + + @Override + public void deviceFound(final BluetoothDevice device, final long timestamp) { + println("****** FOUND__: "+device.toString()); + + if( BluetoothAddressType.BDADDR_LE_PUBLIC != device.getAddressType() + && BLERandomAddressType.STATIC_PUBLIC != device.getBLERandomAddressType() ) { + println("****** FOUND__-2: Skip 'non public' or 'random static public' LE "+device.toString()); + return; + } + if( !devicesInProcessing.contains( device.getAddress() ) && + ( waitForDevices.isEmpty() || + ( waitForDevices.contains( device.getAddress() ) && + ( 0 < MULTI_MEASUREMENTS || !devicesProcessed.contains( device.getAddress() ) ) + ) + ) + ) + { + println("****** FOUND__-0: Connecting "+device.toString()); + { + final long td = BluetoothUtils.getCurrentMilliseconds() - timestamp_t0; // adapter-init -> now + println("PERF: adapter-init -> FOUND__-0 " + td + " ms"); + } + final Thread deviceConnectTask = new Thread( new Runnable() { + @Override + public void run() { + connectDiscoveredDevice(device); + } + }, "DBT-Connect-"+device.getAddress()); + deviceConnectTask.setDaemon(true); // detach thread + deviceConnectTask.start(); + } else { + println("****** FOUND__-1: NOP "+device.toString()); + } + } + + @Override + public void deviceUpdated(final BluetoothDevice device, final EIRDataTypeSet updateMask, final long timestamp) { + if( SHOW_UPDATE_EVENTS ) { + println("****** UPDATED: "+updateMask+" of "+device); + } + } + + @Override + public void deviceConnected(final BluetoothDevice device, final short handle, final long timestamp) { + if( !devicesInProcessing.contains( device.getAddress() ) && + ( waitForDevices.isEmpty() || + ( waitForDevices.contains( device.getAddress() ) && + ( 0 < MULTI_MEASUREMENTS || !devicesProcessed.contains( device.getAddress() ) ) + ) + ) + ) + { + println("****** CONNECTED-0: Processing "+device.toString()); + { + final long td = BluetoothUtils.getCurrentMilliseconds() - timestamp_t0; // adapter-init -> now + println("PERF: adapter-init -> CONNECTED-0 " + td + " ms"); + } + final Thread deviceProcessingTask = new Thread( new Runnable() { + @Override + public void run() { + processConnectedDevice(device); + } + }, "DBT-Process-"+device.getAddress()); + devicesInProcessing.add(device.getAddress()); + deviceProcessingTask.setDaemon(true); // detach thread + deviceProcessingTask.start(); + } else { + println("****** CONNECTED-1: NOP " + device.toString()); + } + } + + @Override + public void deviceDisconnected(final BluetoothDevice device, final HCIStatusCode reason, final short handle, final long timestamp) { + println("****** DISCONNECTED: Reason "+reason+", old handle 0x"+Integer.toHexString(handle)+": "+device+" on "+device.getAdapter()); + + if( REMOVE_DEVICE ) { + final Thread deviceRemoverProcessingTask = new Thread( new Runnable() { + @Override + public void run() { + removeDevice(device); + } + }, "DBT-Remove-"+device.getAddress()); + deviceRemoverProcessingTask.setDaemon(true); // detach thread + deviceRemoverProcessingTask.start(); + } else { + devicesInProcessing.remove(device.getAddress()); + } + } + }; + + private void connectDiscoveredDevice(final BluetoothDevice device) { + println("****** Connecting Device: Start " + device.toString()); + { + final boolean r = device.getAdapter().stopDiscovery(); + println("****** Connecting Device: stopDiscovery result "+r); + } + HCIStatusCode res; + if( !USE_WHITELIST ) { + res = device.connect(); + } else { + res = HCIStatusCode.SUCCESS; + } + println("****** Connecting Device Command, res "+res+": End result "+res+" of " + device.toString()); + if( !USE_WHITELIST && 0 == devicesInProcessing.size() && HCIStatusCode.SUCCESS != res ) { + final boolean r = device.getAdapter().startDiscovery( true ); + println("****** Connecting Device: startDiscovery result "+r); + } + } + + void execute(final Runnable task, final boolean offThread) { + if( offThread ) { + final Thread t = new Thread(task); + t.setDaemon(true); + t.start(); + } else { + task.run(); + } + } + void shutdownTest() { + switch( shutdownTest ) { + case 1: + shutdownTest01(); + break; + case 2: + shutdownTest02(); + break; + default: + // nop + } + } + void shutdownTest01() { + execute( () -> { + final DBTManager mngr = (DBTManager) DBTManager.getManager(); + mngr.shutdown(); + }, true); + } + void shutdownTest02() { + execute( () -> { + System.exit(1); + }, true); + } + + private void processConnectedDevice(final BluetoothDevice device) { + println("****** Processing Device: Start " + device.toString()); + { + // make sure for pending connections on failed connect*(..) command + final boolean r = device.getAdapter().stopDiscovery(); + println("****** Processing Device: stopDiscovery result "+r); + } + + final long t1 = BluetoothUtils.getCurrentMilliseconds(); + boolean success = false; + + // Secure Pairing + { + final List spm = device.getSupportedPairingModes(); + if( !QUIET ) { + println("Supported Secure Pairing Modes: " + spm.toString()); + } + + final List rpm = device.getRequiredPairingModes(); + if( !QUIET ) { + println("Required Secure Pairing Modes: " + rpm.toString()); + } + + if( spm.contains(PairingMode.JUST_WORKS) ) { + final HCIStatusCode res = device.pair(null); // empty for JustWorks + println("Secure Pairing Just Works result " + res + " of " + device); + } else if( spm.contains(PairingMode.PASSKEY_ENTRY) ) { + final HCIStatusCode res = device.pair("111111"); // PasskeyEntry + println("Secure Pairing Passkey Entry result " + res + " of " + device); + } else if( !QUIET ) { + println("Secure Pairing JUST_WORKS or PASSKEY_ENTRY not supported, but " + spm.toString() + " on " + device); + } + } + + // + // GATT Service Processing + // + try { + final List primServices = device.getServices(); // implicit GATT connect... + if( null == primServices || 0 == primServices.size() ) { + // Cheating the flow, but avoiding: goto, do-while-false and lastly unreadable intendations + // And it is an error case nonetheless ;-) + throw new RuntimeException("Processing Device: getServices() failed " + device.toString()); + } + final long t5 = BluetoothUtils.getCurrentMilliseconds(); + if( !QUIET ) { + final long td01 = t1 - timestamp_t0; // adapter-init -> processing-start + final long td15 = t5 - t1; // get-gatt-services + final long tdc5 = t5 - device.getLastDiscoveryTimestamp(); // discovered to gatt-complete + final long td05 = t5 - timestamp_t0; // adapter-init -> gatt-complete + println(System.lineSeparator()+System.lineSeparator()); + println("PERF: GATT primary-services completed\n"); + println("PERF: adapter-init to processing-start " + td01 + " ms,"+System.lineSeparator()+ + "PERF: get-gatt-services " + td15 + " ms,"+System.lineSeparator()+ + "PERF: discovered to gatt-complete " + tdc5 + " ms (connect " + (tdc5 - td15) + " ms),"+System.lineSeparator()+ + "PERF: adapter-init to gatt-complete " + td05 + " ms"+System.lineSeparator()); + } + { + // WIP: Implement a simple Characteristic ping-pong writeValue <-> notify transmission for stress testing. + final BluetoothManager manager = device.getAdapter().getManager(); + for(final String characteristic : charIdentifierList) { + final BluetoothGattCharacteristic char2 = (BluetoothGattCharacteristic) + manager.find(BluetoothType.GATT_CHARACTERISTIC, null, characteristic, device); + println("Char UUID "+characteristic); + println(" over device : "+char2); + if( null != char2 ) { + final GATTCharacteristicListener charPingPongListener = new GATTCharacteristicListener(null) { + @Override + public void notificationReceived(final BluetoothGattCharacteristic charDecl, + final byte[] value, final long timestamp) { + println("****** PingPong GATT notificationReceived: "+charDecl+ + ", value "+BluetoothUtils.bytesHexString(value, true, true)); + } + + @Override + public void indicationReceived(final BluetoothGattCharacteristic charDecl, + final byte[] value, final long timestamp, final boolean confirmationSent) { + println("****** PingPong GATT indicationReceived: "+charDecl+ + ", value "+BluetoothUtils.bytesHexString(value, true, true)); + } + }; + final boolean enabledState[] = { false, false }; + final boolean addedCharPingPongListenerRes = + char2.addCharacteristicListener(charPingPongListener, enabledState); + BluetoothGattService.addCharacteristicListenerToAll(device, primServices, charPingPongListener); + if( !QUIET ) { + println("Added CharPingPongListenerRes: "+addedCharPingPongListenerRes+", enabledState "+Arrays.toString(enabledState)); + } + } + } + } + { + final GATTCharacteristicListener myCharacteristicListener = new GATTCharacteristicListener(null) { + @Override + public void notificationReceived(final BluetoothGattCharacteristic charDecl, + final byte[] value, final long timestamp) { + println("****** GATT notificationReceived: "+charDecl+ + ", value "+BluetoothUtils.bytesHexString(value, true, true)); + shutdownTest(); + + } + + @Override + public void indicationReceived(final BluetoothGattCharacteristic charDecl, + final byte[] value, final long timestamp, final boolean confirmationSent) { + println("****** GATT indicationReceived: "+charDecl+ + ", value "+BluetoothUtils.bytesHexString(value, true, true)); + shutdownTest(); + } + }; + final boolean addedCharacteristicListenerRes = + BluetoothGattService.addCharacteristicListenerToAll(device, primServices, myCharacteristicListener); + if( !QUIET ) { + println("Added GATTCharacteristicListener: "+addedCharacteristicListenerRes); + } + } + + try { + int i=0; + for(final Iterator srvIter = primServices.iterator(); srvIter.hasNext(); i++) { + final BluetoothGattService primService = srvIter.next(); + if( !QUIET ) { + printf(" [%02d] Service %s\n", i, primService.toString()); + printf(" [%02d] Service Characteristics\n", i); + } + int j=0; + final List serviceCharacteristics = primService.getCharacteristics(); + for(final Iterator charIter = serviceCharacteristics.iterator(); charIter.hasNext(); j++) { + final BluetoothGattCharacteristic serviceChar = charIter.next(); + if( !QUIET ) { + printf(" [%02d.%02d] CharDef: %s\n", i, j, serviceChar.toString()); + } + final List properties = Arrays.asList(serviceChar.getFlags()); + if( properties.contains("read") ) { + final byte[] value = serviceChar.readValue(); + final String svalue = BluetoothUtils.decodeUTF8String(value, 0, value.length); + if( !QUIET ) { + printf(" [%02d.%02d] CharVal: %s ('%s')\n", + i, j, BluetoothUtils.bytesHexString(value, true, true), svalue); + } + } + int k=0; + final List charDescList = serviceChar.getDescriptors(); + for(final Iterator descIter = charDescList.iterator(); descIter.hasNext(); k++) { + final BluetoothGattDescriptor charDesc = descIter.next(); + if( !QUIET ) { + printf(" [%02d.%02d.%02d] Desc: %s\n", i, j, k, charDesc.toString()); + } + } + } + } + } catch( final Exception ex) { + println("Caught "+ex.getMessage()); + ex.printStackTrace(); + } + // FIXME sleep 1s for potential callbacks .. + try { + Thread.sleep(1000); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + success = true; + } catch (final Throwable t ) { + println("****** Processing Device: Exception caught for " + device.toString() + ": "+t.getMessage()); + t.printStackTrace(); + } + + if( !USE_WHITELIST && 0 == devicesInProcessing.size() ) { + final boolean r = device.getAdapter().startDiscovery( true ); + println("****** Processing Device: startDiscovery result "+r); + } + + if( KEEP_CONNECTED && GATT_PING_ENABLED && success ) { + while( device.pingGATT() ) { + println("****** Processing Device: pingGATT OK: "+device.getAddress()); + try { + Thread.sleep(1000); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + println("****** Processing Device: pingGATT failed, waiting for disconnect: "+device.getAddress()); + // Even w/ GATT_PING_ENABLED, we utilize disconnect event to clean up -> remove + } + + if( 0 < MULTI_MEASUREMENTS ) { + MULTI_MEASUREMENTS--; + println("****** Processing Device: MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS+": "+device.getAddress()); + } + + println("****** Processing Device: End: Success " + success + + " on " + device.toString() + "; devInProc "+devicesInProcessing.size()); + if( success ) { + devicesProcessed.add(device.getAddress()); + } + + if( !KEEP_CONNECTED ) { + devicesInProcessing.remove(device.getAddress()); + + device.remove(); + + if( !USE_WHITELIST && 0 == devicesInProcessing.size() ) { + final boolean r = device.getAdapter().startDiscovery( true ); + println("****** Processing Device: startDiscovery result "+r); + } + } + } + + private void removeDevice(final BluetoothDevice device) { + println("****** Remove Device: removing: "+device.getAddress()); + device.getAdapter().stopDiscovery(); + + devicesInProcessing.remove(device.getAddress()); + + device.remove(); + + if( !USE_WHITELIST && 0 == devicesInProcessing.size() ) { + final boolean r = device.getAdapter().startDiscovery( true ); + println("****** Remove Device: startDiscovery result "+r); + } + } + + public void runTest(final BluetoothManager manager) { + final BluetoothAdapter adapter; + { + final List adapters = manager.getAdapters(); + for(int i=0; i < adapters.size(); i++) { + println("Adapter["+i+"]: "+adapters.get(i)); + } + if( adapters.size() <= dev_id ) { + println("No adapter dev_id "+dev_id+" available, adapter count "+adapters.size()); + System.exit(-1); + } + if( 0 > dev_id ) { + adapter = manager.getDefaultAdapter(); + } else { + adapter = adapters.get(dev_id); + } + if( !adapter.isEnabled() ) { + println("Adapter not enabled: device "+adapter.getName()+", address "+adapter.getAddress()+": "+adapter.toString()); + System.exit(-1); + } + } + + timestamp_t0 = BluetoothUtils.getCurrentMilliseconds(); + + adapter.addStatusListener(statusListener, null); + adapter.enableDiscoverableNotifications(new BooleanNotification("Discoverable", timestamp_t0)); + + adapter.enableDiscoveringNotifications(new BooleanNotification("Discovering", timestamp_t0)); + + adapter.enablePairableNotifications(new BooleanNotification("Pairable", timestamp_t0)); + + adapter.enablePoweredNotifications(new BooleanNotification("Powered", timestamp_t0)); + + boolean done = false; + + if( USE_WHITELIST ) { + for(final Iterator wliter = whitelist.iterator(); wliter.hasNext(); ) { + final String addr = wliter.next(); + final boolean res = adapter.addDeviceToWhitelist(addr, BluetoothAddressType.BDADDR_LE_PUBLIC, HCIWhitelistConnectType.HCI_AUTO_CONN_ALWAYS); + println("Added to whitelist: res "+res+", address "+addr); + } + } else { + if( !adapter.startDiscovery( true ) ) { + println("Adapter start discovery failed"); + done = true; + } + } + + while( !done ) { + if( 0 == MULTI_MEASUREMENTS || + ( -1 == MULTI_MEASUREMENTS && !waitForDevices.isEmpty() && devicesProcessed.containsAll(waitForDevices) ) + ) + { + println("****** EOL Test MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS+ + ", processed "+devicesProcessed.size()+"/"+waitForDevices.size()); + println("****** WaitForDevices "+Arrays.toString(waitForDevices.toArray())); + println("****** DevicesProcessed "+Arrays.toString(devicesProcessed.toArray())); + done = true; + } else { + try { + Thread.sleep(3000); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + } + + // All implicit via destructor or shutdown hook! + // manager.shutdown(); /* implies: adapter.close(); */ + } + + public static void main(final String[] args) throws InterruptedException { + for(int i=0; i< args.length; i++) { + final String arg = args[i]; + if( arg.equals("-debug") ) { + System.setProperty("org.tinyb.verbose", "true"); + System.setProperty("org.tinyb.debug", "true"); + } else if( arg.equals("-verbose") ) { + System.setProperty("org.tinyb.verbose", "true"); + } else if( arg.equals("-dbt_debug") && args.length > (i+1) ) { + System.setProperty("direct_bt.debug", args[++i]); + } else if( arg.equals("-dbt_verbose") && args.length > (i+1) ) { + System.setProperty("direct_bt.verbose", args[++i]); + } else if( arg.equals("-dbt_gatt") && args.length > (i+1) ) { + System.setProperty("direct_bt.gatt", args[++i]); + } else if( arg.equals("-dbt_l2cap") && args.length > (i+1) ) { + System.setProperty("direct_bt.l2cap", args[++i]); + } else if( arg.equals("-dbt_hci") && args.length > (i+1) ) { + System.setProperty("direct_bt.hci", args[++i]); + } else if( arg.equals("-dbt_mgmt") && args.length > (i+1) ) { + System.setProperty("direct_bt.mgmt", args[++i]); + } else if( arg.equals("-default_dev_id") && args.length > (i+1) ) { + final int default_dev_id = Integer.valueOf(args[++i]).intValue(); + if( 0 <= default_dev_id ) { + System.setProperty("org.tinyb.default_adapter", String.valueOf(default_dev_id)); + System.err.println("Setting 'org.tinyb.default_adapter' to "+default_dev_id); + } + } else if( arg.equals("-btmode") && args.length > (i+1) ) { + final BTMode btmode = BTMode.get(args[++i]); + System.setProperty("org.tinyb.btmode", btmode.toString()); + System.err.println("Setting 'org.tinyb.btmode' to "+btmode.toString()); + } + } + final BluetoothManager manager; + try { + manager = BluetoothFactory.getDirectBTBluetoothManager(); + } catch (BluetoothException | NoSuchMethodException | SecurityException + | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | ClassNotFoundException e) { + System.err.println("Unable to instantiate DirectBT BluetoothManager"); + e.printStackTrace(); + System.exit(-1); + return; + } + println("DirectBT BluetoothManager initialized!"); + final DBTScanner10 test = new DBTScanner10(); + + boolean waitForEnter=false; + { + for(int i=0; i< args.length; i++) { + final String arg = args[i]; + + if( arg.equals("-wait") ) { + waitForEnter = true; + } else if( arg.equals("-show_update_events") ) { + test.SHOW_UPDATE_EVENTS = true; + } else if( arg.equals("-quiet") ) { + test.QUIET = true; + } else if( arg.equals("-dev_id") && args.length > (i+1) ) { + test.dev_id = Integer.valueOf(args[++i]).intValue(); + } else if( arg.equals("-shutdown") && args.length > (i+1) ) { + test.shutdownTest = Integer.valueOf(args[++i]).intValue(); + } else if( arg.equals("-mac") && args.length > (i+1) ) { + test.waitForDevices.add(args[++i]); + } else if( arg.equals("-wl") && args.length > (i+1) ) { + final String addr = args[++i]; + println("Whitelist + "+addr); + test.whitelist.add(addr); + test.USE_WHITELIST = true; + } else if( arg.equals("-char") && args.length > (i+1) ) { + test.charIdentifierList.add(args[++i]); + } else if( arg.equals("-disconnect") ) { + test.KEEP_CONNECTED = false; + } else if( arg.equals("-enableGATTPing") ) { + test.GATT_PING_ENABLED = true; + } else if( arg.equals("-keepDevice") ) { + test.REMOVE_DEVICE = false; + } else if( arg.equals("-count") && args.length > (i+1) ) { + test.MULTI_MEASUREMENTS = Integer.valueOf(args[++i]).intValue(); + } else if( arg.equals("-single") ) { + test.MULTI_MEASUREMENTS = -1; + } + } + println("Run with '[-default_dev_id ] [-dev_id ] [-btmode LE|BREDR|DUAL] "+ + "[-bluetoothManager ] "+ + "[-disconnect] [-enableGATTPing] [-count ] [-single] (-char )* [-show_update_events] [-quiet]"+ + "(-mac )* (-wl )* "+ + "[-verbose] [-debug] "+ + "[-dbt_verbose true|false] "+ + "[-dbt_debug true|false|adapter.event,gatt.data,hci.event,mgmt.event] "+ + "[-dbt_mgmt cmd.timeout=3000,ringsize=64,...] "+ + "[-dbt_hci cmd.complete.timeout=10000,cmd.status.timeout=3000,ringsize=64,...] "+ + "[-dbt_gatt cmd.read.timeout=500,cmd.write.timeout=500,cmd.init.timeout=2500,ringsize=128,...] "+ + "[-dbt_l2cap reader.timeout=10000,restart.count=0,...] "+ + "[-shutdown ]'"); + } + + println("MULTI_MEASUREMENTS "+test.MULTI_MEASUREMENTS); + println("KEEP_CONNECTED "+test.KEEP_CONNECTED); + println("GATT_PING_ENABLED "+test.GATT_PING_ENABLED); + println("REMOVE_DEVICE "+test.REMOVE_DEVICE); + println("USE_WHITELIST "+test.USE_WHITELIST); + println("SHOW_UPDATE_EVENTS "+test.SHOW_UPDATE_EVENTS); + println("QUIET "+test.QUIET); + + println("dev_id "+test.dev_id); + println("waitForDevice: "+Arrays.toString(test.waitForDevices.toArray())); + println("characteristicList: "+Arrays.toString(test.charIdentifierList.toArray())); + + if( waitForEnter ) { + println("Press ENTER to continue\n"); + try{ System.in.read(); + } catch(final Exception e) { } + } + test.runTest(manager); + } + + static class BooleanNotification implements BluetoothNotification { + private final long t0; + private final String name; + private boolean v; + + public BooleanNotification(final String name, final long t0) { + this.t0 = t0; + this.name = name; + this.v = false; + } + + @Override + public void run(final Boolean v) { + synchronized(this) { + final long t1 = BluetoothUtils.getCurrentMilliseconds(); + this.v = v.booleanValue(); + System.out.println("###### "+name+": "+v+" in td "+(t1-t0)+" ms!"); + this.notifyAll(); + } + } + public boolean getValue() { + synchronized(this) { + return v; + } + } + } +} diff --git a/examples/java/ScannerTinyB10.java b/examples/java/ScannerTinyB10.java deleted file mode 100644 index c6692b7d..00000000 --- a/examples/java/ScannerTinyB10.java +++ /dev/null @@ -1,704 +0,0 @@ -/** - * Author: Sven Gothel - * Copyright (c) 2020 Gothel Software e.K. - * Copyright (c) 2020 ZAFENA AB - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import org.tinyb.AdapterSettings; -import org.tinyb.BluetoothAdapter; -import org.tinyb.BluetoothAddressType; -import org.tinyb.BluetoothDevice; -import org.tinyb.AdapterStatusListener; -import org.tinyb.BLERandomAddressType; -import org.tinyb.BTMode; -import org.tinyb.BluetoothException; -import org.tinyb.BluetoothFactory; -import org.tinyb.BluetoothGattCharacteristic; -import org.tinyb.BluetoothGattDescriptor; -import org.tinyb.BluetoothGattService; -import org.tinyb.BluetoothManager; -import org.tinyb.BluetoothNotification; -import org.tinyb.BluetoothType; -import org.tinyb.BluetoothUtils; -import org.tinyb.EIRDataTypeSet; -import org.tinyb.GATTCharacteristicListener; -import org.tinyb.HCIStatusCode; -import org.tinyb.HCIWhitelistConnectType; -import org.tinyb.PairingMode; - -import direct_bt.tinyb.DBTManager; - -/** - * This Java scanner example uses the Direct-BT fully event driven workflow - * and adds multithreading, i.e. one thread processes each found device found - * as notified via the event listener. - *

- * This example represents the recommended utilization of Direct-BT. - *

- */ -public class ScannerTinyB10 { - static final String EUI48_ANY_DEVICE = "00:00:00:00:00:00"; - - final List waitForDevices = new ArrayList(); - - long timestamp_t0; - - int MULTI_MEASUREMENTS = 8; - boolean KEEP_CONNECTED = true; - boolean GATT_PING_ENABLED = false; - boolean REMOVE_DEVICE = true; - boolean USE_WHITELIST = false; - final List whitelist = new ArrayList(); - final List charIdentifierList = new ArrayList(); - final List charValueList = new ArrayList(); - - boolean SHOW_UPDATE_EVENTS = false; - boolean QUIET = false; - - int dev_id = -1; // use default - - int shutdownTest = 0; - - static void printf(final String format, final Object... args) { - final Object[] args2 = new Object[args.length+1]; - args2[0] = BluetoothUtils.getElapsedMillisecond(); - System.arraycopy(args, 0, args2, 1, args.length); - System.err.printf("[%,9d] "+format, args2); - // System.err.printf("[%,9d] ", BluetoothUtils.getElapsedMillisecond()); - // System.err.printf(format, args); - } - static void println(final String msg) { - System.err.printf("[%,9d] %s%s", BluetoothUtils.getElapsedMillisecond(), msg, System.lineSeparator()); - } - - - Collection devicesInProcessing = Collections.synchronizedCollection(new ArrayList<>()); - Collection devicesProcessed = Collections.synchronizedCollection(new ArrayList<>()); - - final AdapterStatusListener statusListener = new AdapterStatusListener() { - @Override - public void adapterSettingsChanged(final BluetoothAdapter adapter, final AdapterSettings oldmask, - final AdapterSettings newmask, final AdapterSettings changedmask, final long timestamp) { - println("****** SETTINGS: "+oldmask+" -> "+newmask+", changed "+changedmask); - println("Status Adapter:"); - println(adapter.toString()); - if( changedmask.isSet(AdapterSettings.SettingType.POWERED) && - newmask.isSet(AdapterSettings.SettingType.POWERED) ) - { - // powered on adapter .... - if( !adapter.startDiscovery( true ) ) { - println("Adapter (powered-on): Start discovery failed"); - } - } - } - - @Override - public void discoveringChanged(final BluetoothAdapter adapter, final boolean enabled, final boolean keepAlive, final long timestamp) { - println("****** DISCOVERING: enabled "+enabled+", keepAlive "+keepAlive+" on "+adapter); - } - - @Override - public void deviceFound(final BluetoothDevice device, final long timestamp) { - println("****** FOUND__: "+device.toString()); - - if( BluetoothAddressType.BDADDR_LE_PUBLIC != device.getAddressType() - && BLERandomAddressType.STATIC_PUBLIC != device.getBLERandomAddressType() ) { - println("****** FOUND__-2: Skip 'non public' or 'random static public' LE "+device.toString()); - return; - } - if( !devicesInProcessing.contains( device.getAddress() ) && - ( waitForDevices.isEmpty() || - ( waitForDevices.contains( device.getAddress() ) && - ( 0 < MULTI_MEASUREMENTS || !devicesProcessed.contains( device.getAddress() ) ) - ) - ) - ) - { - println("****** FOUND__-0: Connecting "+device.toString()); - { - final long td = BluetoothUtils.getCurrentMilliseconds() - timestamp_t0; // adapter-init -> now - println("PERF: adapter-init -> FOUND__-0 " + td + " ms"); - } - final Thread deviceConnectTask = new Thread( new Runnable() { - @Override - public void run() { - connectDiscoveredDevice(device); - } - }, "DBT-Connect-"+device.getAddress()); - deviceConnectTask.setDaemon(true); // detach thread - deviceConnectTask.start(); - } else { - println("****** FOUND__-1: NOP "+device.toString()); - } - } - - @Override - public void deviceUpdated(final BluetoothDevice device, final EIRDataTypeSet updateMask, final long timestamp) { - if( SHOW_UPDATE_EVENTS ) { - println("****** UPDATED: "+updateMask+" of "+device); - } - } - - @Override - public void deviceConnected(final BluetoothDevice device, final short handle, final long timestamp) { - if( !devicesInProcessing.contains( device.getAddress() ) && - ( waitForDevices.isEmpty() || - ( waitForDevices.contains( device.getAddress() ) && - ( 0 < MULTI_MEASUREMENTS || !devicesProcessed.contains( device.getAddress() ) ) - ) - ) - ) - { - println("****** CONNECTED-0: Processing "+device.toString()); - { - final long td = BluetoothUtils.getCurrentMilliseconds() - timestamp_t0; // adapter-init -> now - println("PERF: adapter-init -> CONNECTED-0 " + td + " ms"); - } - final Thread deviceProcessingTask = new Thread( new Runnable() { - @Override - public void run() { - processConnectedDevice(device); - } - }, "DBT-Process-"+device.getAddress()); - devicesInProcessing.add(device.getAddress()); - deviceProcessingTask.setDaemon(true); // detach thread - deviceProcessingTask.start(); - } else { - println("****** CONNECTED-1: NOP " + device.toString()); - } - } - - @Override - public void deviceDisconnected(final BluetoothDevice device, final HCIStatusCode reason, final short handle, final long timestamp) { - println("****** DISCONNECTED: Reason "+reason+", old handle 0x"+Integer.toHexString(handle)+": "+device+" on "+device.getAdapter()); - - if( REMOVE_DEVICE ) { - final Thread deviceRemoverProcessingTask = new Thread( new Runnable() { - @Override - public void run() { - removeDevice(device); - } - }, "DBT-Remove-"+device.getAddress()); - deviceRemoverProcessingTask.setDaemon(true); // detach thread - deviceRemoverProcessingTask.start(); - } else { - devicesInProcessing.remove(device.getAddress()); - } - } - }; - - private void connectDiscoveredDevice(final BluetoothDevice device) { - println("****** Connecting Device: Start " + device.toString()); - { - final boolean r = device.getAdapter().stopDiscovery(); - println("****** Connecting Device: stopDiscovery result "+r); - } - HCIStatusCode res; - if( !USE_WHITELIST ) { - res = device.connect(); - } else { - res = HCIStatusCode.SUCCESS; - } - println("****** Connecting Device Command, res "+res+": End result "+res+" of " + device.toString()); - if( !USE_WHITELIST && 0 == devicesInProcessing.size() && HCIStatusCode.SUCCESS != res ) { - final boolean r = device.getAdapter().startDiscovery( true ); - println("****** Connecting Device: startDiscovery result "+r); - } - } - - void execute(final Runnable task, final boolean offThread) { - if( offThread ) { - final Thread t = new Thread(task); - t.setDaemon(true); - t.start(); - } else { - task.run(); - } - } - void shutdownTest() { - switch( shutdownTest ) { - case 1: - shutdownTest01(); - break; - case 2: - shutdownTest02(); - break; - default: - // nop - } - } - void shutdownTest01() { - execute( () -> { - final DBTManager mngr = (DBTManager) DBTManager.getManager(); - mngr.shutdown(); - }, true); - } - void shutdownTest02() { - execute( () -> { - System.exit(1); - }, true); - } - - private void processConnectedDevice(final BluetoothDevice device) { - println("****** Processing Device: Start " + device.toString()); - { - // make sure for pending connections on failed connect*(..) command - final boolean r = device.getAdapter().stopDiscovery(); - println("****** Processing Device: stopDiscovery result "+r); - } - - final long t1 = BluetoothUtils.getCurrentMilliseconds(); - boolean success = false; - - // Secure Pairing - { - final List spm = device.getSupportedPairingModes(); - if( !QUIET ) { - println("Supported Secure Pairing Modes: " + spm.toString()); - } - - final List rpm = device.getRequiredPairingModes(); - if( !QUIET ) { - println("Required Secure Pairing Modes: " + rpm.toString()); - } - - if( spm.contains(PairingMode.JUST_WORKS) ) { - final HCIStatusCode res = device.pair(null); // empty for JustWorks - println("Secure Pairing Just Works result " + res + " of " + device); - } else if( spm.contains(PairingMode.PASSKEY_ENTRY) ) { - final HCIStatusCode res = device.pair("111111"); // PasskeyEntry - println("Secure Pairing Passkey Entry result " + res + " of " + device); - } else if( !QUIET ) { - println("Secure Pairing JUST_WORKS or PASSKEY_ENTRY not supported, but " + spm.toString() + " on " + device); - } - } - - // - // GATT Service Processing - // - try { - final List primServices = device.getServices(); // implicit GATT connect... - if( null == primServices || 0 == primServices.size() ) { - // Cheating the flow, but avoiding: goto, do-while-false and lastly unreadable intendations - // And it is an error case nonetheless ;-) - throw new RuntimeException("Processing Device: getServices() failed " + device.toString()); - } - final long t5 = BluetoothUtils.getCurrentMilliseconds(); - if( !QUIET ) { - final long td01 = t1 - timestamp_t0; // adapter-init -> processing-start - final long td15 = t5 - t1; // get-gatt-services - final long tdc5 = t5 - device.getLastDiscoveryTimestamp(); // discovered to gatt-complete - final long td05 = t5 - timestamp_t0; // adapter-init -> gatt-complete - println(System.lineSeparator()+System.lineSeparator()); - println("PERF: GATT primary-services completed\n"); - println("PERF: adapter-init to processing-start " + td01 + " ms,"+System.lineSeparator()+ - "PERF: get-gatt-services " + td15 + " ms,"+System.lineSeparator()+ - "PERF: discovered to gatt-complete " + tdc5 + " ms (connect " + (tdc5 - td15) + " ms),"+System.lineSeparator()+ - "PERF: adapter-init to gatt-complete " + td05 + " ms"+System.lineSeparator()); - } - { - // WIP: Implement a simple Characteristic ping-pong writeValue <-> notify transmission for stress testing. - final BluetoothManager manager = device.getAdapter().getManager(); - for(final String characteristic : charIdentifierList) { - final BluetoothGattCharacteristic char2 = (BluetoothGattCharacteristic) - manager.find(BluetoothType.GATT_CHARACTERISTIC, null, characteristic, device); - println("Char UUID "+characteristic); - println(" over device : "+char2); - if( null != char2 ) { - final GATTCharacteristicListener charPingPongListener = new GATTCharacteristicListener(null) { - @Override - public void notificationReceived(final BluetoothGattCharacteristic charDecl, - final byte[] value, final long timestamp) { - println("****** PingPong GATT notificationReceived: "+charDecl+ - ", value "+BluetoothUtils.bytesHexString(value, true, true)); - } - - @Override - public void indicationReceived(final BluetoothGattCharacteristic charDecl, - final byte[] value, final long timestamp, final boolean confirmationSent) { - println("****** PingPong GATT indicationReceived: "+charDecl+ - ", value "+BluetoothUtils.bytesHexString(value, true, true)); - } - }; - final boolean enabledState[] = { false, false }; - final boolean addedCharPingPongListenerRes = - char2.addCharacteristicListener(charPingPongListener, enabledState); - BluetoothGattService.addCharacteristicListenerToAll(device, primServices, charPingPongListener); - if( !QUIET ) { - println("Added CharPingPongListenerRes: "+addedCharPingPongListenerRes+", enabledState "+Arrays.toString(enabledState)); - } - } - } - } - { - final GATTCharacteristicListener myCharacteristicListener = new GATTCharacteristicListener(null) { - @Override - public void notificationReceived(final BluetoothGattCharacteristic charDecl, - final byte[] value, final long timestamp) { - println("****** GATT notificationReceived: "+charDecl+ - ", value "+BluetoothUtils.bytesHexString(value, true, true)); - shutdownTest(); - - } - - @Override - public void indicationReceived(final BluetoothGattCharacteristic charDecl, - final byte[] value, final long timestamp, final boolean confirmationSent) { - println("****** GATT indicationReceived: "+charDecl+ - ", value "+BluetoothUtils.bytesHexString(value, true, true)); - shutdownTest(); - } - }; - final boolean addedCharacteristicListenerRes = - BluetoothGattService.addCharacteristicListenerToAll(device, primServices, myCharacteristicListener); - if( !QUIET ) { - println("Added GATTCharacteristicListener: "+addedCharacteristicListenerRes); - } - } - - try { - int i=0; - for(final Iterator srvIter = primServices.iterator(); srvIter.hasNext(); i++) { - final BluetoothGattService primService = srvIter.next(); - if( !QUIET ) { - printf(" [%02d] Service %s\n", i, primService.toString()); - printf(" [%02d] Service Characteristics\n", i); - } - int j=0; - final List serviceCharacteristics = primService.getCharacteristics(); - for(final Iterator charIter = serviceCharacteristics.iterator(); charIter.hasNext(); j++) { - final BluetoothGattCharacteristic serviceChar = charIter.next(); - if( !QUIET ) { - printf(" [%02d.%02d] CharDef: %s\n", i, j, serviceChar.toString()); - } - final List properties = Arrays.asList(serviceChar.getFlags()); - if( properties.contains("read") ) { - final byte[] value = serviceChar.readValue(); - final String svalue = BluetoothUtils.decodeUTF8String(value, 0, value.length); - if( !QUIET ) { - printf(" [%02d.%02d] CharVal: %s ('%s')\n", - i, j, BluetoothUtils.bytesHexString(value, true, true), svalue); - } - } - int k=0; - final List charDescList = serviceChar.getDescriptors(); - for(final Iterator descIter = charDescList.iterator(); descIter.hasNext(); k++) { - final BluetoothGattDescriptor charDesc = descIter.next(); - if( !QUIET ) { - printf(" [%02d.%02d.%02d] Desc: %s\n", i, j, k, charDesc.toString()); - } - } - } - } - } catch( final Exception ex) { - println("Caught "+ex.getMessage()); - ex.printStackTrace(); - } - // FIXME sleep 1s for potential callbacks .. - try { - Thread.sleep(1000); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - success = true; - } catch (final Throwable t ) { - println("****** Processing Device: Exception caught for " + device.toString() + ": "+t.getMessage()); - t.printStackTrace(); - } - - if( !USE_WHITELIST && 0 == devicesInProcessing.size() ) { - final boolean r = device.getAdapter().startDiscovery( true ); - println("****** Processing Device: startDiscovery result "+r); - } - - if( KEEP_CONNECTED && GATT_PING_ENABLED && success ) { - while( device.pingGATT() ) { - println("****** Processing Device: pingGATT OK: "+device.getAddress()); - try { - Thread.sleep(1000); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } - println("****** Processing Device: pingGATT failed, waiting for disconnect: "+device.getAddress()); - // Even w/ GATT_PING_ENABLED, we utilize disconnect event to clean up -> remove - } - - if( 0 < MULTI_MEASUREMENTS ) { - MULTI_MEASUREMENTS--; - println("****** Processing Device: MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS+": "+device.getAddress()); - } - - println("****** Processing Device: End: Success " + success + - " on " + device.toString() + "; devInProc "+devicesInProcessing.size()); - if( success ) { - devicesProcessed.add(device.getAddress()); - } - - if( !KEEP_CONNECTED ) { - devicesInProcessing.remove(device.getAddress()); - - device.remove(); - - if( !USE_WHITELIST && 0 == devicesInProcessing.size() ) { - final boolean r = device.getAdapter().startDiscovery( true ); - println("****** Processing Device: startDiscovery result "+r); - } - } - } - - private void removeDevice(final BluetoothDevice device) { - println("****** Remove Device: removing: "+device.getAddress()); - device.getAdapter().stopDiscovery(); - - devicesInProcessing.remove(device.getAddress()); - - device.remove(); - - if( !USE_WHITELIST && 0 == devicesInProcessing.size() ) { - final boolean r = device.getAdapter().startDiscovery( true ); - println("****** Remove Device: startDiscovery result "+r); - } - } - - public void runTest(final BluetoothManager manager) { - final BluetoothAdapter adapter; - { - final List adapters = manager.getAdapters(); - for(int i=0; i < adapters.size(); i++) { - println("Adapter["+i+"]: "+adapters.get(i)); - } - if( adapters.size() <= dev_id ) { - println("No adapter dev_id "+dev_id+" available, adapter count "+adapters.size()); - System.exit(-1); - } - if( 0 > dev_id ) { - adapter = manager.getDefaultAdapter(); - } else { - adapter = adapters.get(dev_id); - } - if( !adapter.isEnabled() ) { - println("Adapter not enabled: device "+adapter.getName()+", address "+adapter.getAddress()+": "+adapter.toString()); - System.exit(-1); - } - } - - timestamp_t0 = BluetoothUtils.getCurrentMilliseconds(); - - adapter.addStatusListener(statusListener, null); - adapter.enableDiscoverableNotifications(new BooleanNotification("Discoverable", timestamp_t0)); - - adapter.enableDiscoveringNotifications(new BooleanNotification("Discovering", timestamp_t0)); - - adapter.enablePairableNotifications(new BooleanNotification("Pairable", timestamp_t0)); - - adapter.enablePoweredNotifications(new BooleanNotification("Powered", timestamp_t0)); - - boolean done = false; - - if( USE_WHITELIST ) { - for(final Iterator wliter = whitelist.iterator(); wliter.hasNext(); ) { - final String addr = wliter.next(); - final boolean res = adapter.addDeviceToWhitelist(addr, BluetoothAddressType.BDADDR_LE_PUBLIC, HCIWhitelistConnectType.HCI_AUTO_CONN_ALWAYS); - println("Added to whitelist: res "+res+", address "+addr); - } - } else { - if( !adapter.startDiscovery( true ) ) { - println("Adapter start discovery failed"); - done = true; - } - } - - while( !done ) { - if( 0 == MULTI_MEASUREMENTS || - ( -1 == MULTI_MEASUREMENTS && !waitForDevices.isEmpty() && devicesProcessed.containsAll(waitForDevices) ) - ) - { - println("****** EOL Test MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS+ - ", processed "+devicesProcessed.size()+"/"+waitForDevices.size()); - println("****** WaitForDevices "+Arrays.toString(waitForDevices.toArray())); - println("****** DevicesProcessed "+Arrays.toString(devicesProcessed.toArray())); - done = true; - } else { - try { - Thread.sleep(3000); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } - } - - // All implicit via destructor or shutdown hook! - // manager.shutdown(); /* implies: adapter.close(); */ - } - - public static void main(final String[] args) throws InterruptedException { - for(int i=0; i< args.length; i++) { - final String arg = args[i]; - if( arg.equals("-debug") ) { - System.setProperty("org.tinyb.verbose", "true"); - System.setProperty("org.tinyb.debug", "true"); - } else if( arg.equals("-verbose") ) { - System.setProperty("org.tinyb.verbose", "true"); - } else if( arg.equals("-dbt_debug") && args.length > (i+1) ) { - System.setProperty("direct_bt.debug", args[++i]); - } else if( arg.equals("-dbt_verbose") && args.length > (i+1) ) { - System.setProperty("direct_bt.verbose", args[++i]); - } else if( arg.equals("-dbt_gatt") && args.length > (i+1) ) { - System.setProperty("direct_bt.gatt", args[++i]); - } else if( arg.equals("-dbt_l2cap") && args.length > (i+1) ) { - System.setProperty("direct_bt.l2cap", args[++i]); - } else if( arg.equals("-dbt_hci") && args.length > (i+1) ) { - System.setProperty("direct_bt.hci", args[++i]); - } else if( arg.equals("-dbt_mgmt") && args.length > (i+1) ) { - System.setProperty("direct_bt.mgmt", args[++i]); - } else if( arg.equals("-default_dev_id") && args.length > (i+1) ) { - final int default_dev_id = Integer.valueOf(args[++i]).intValue(); - if( 0 <= default_dev_id ) { - System.setProperty("org.tinyb.default_adapter", String.valueOf(default_dev_id)); - System.err.println("Setting 'org.tinyb.default_adapter' to "+default_dev_id); - } - } else if( arg.equals("-btmode") && args.length > (i+1) ) { - final BTMode btmode = BTMode.get(args[++i]); - System.setProperty("org.tinyb.btmode", btmode.toString()); - System.err.println("Setting 'org.tinyb.btmode' to "+btmode.toString()); - } - } - final BluetoothManager manager; - try { - manager = BluetoothFactory.getDirectBTBluetoothManager(); - } catch (BluetoothException | NoSuchMethodException | SecurityException - | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | ClassNotFoundException e) { - System.err.println("Unable to instantiate DirectBT BluetoothManager"); - e.printStackTrace(); - System.exit(-1); - return; - } - println("DirectBT BluetoothManager initialized!"); - final ScannerTinyB10 test = new ScannerTinyB10(); - - boolean waitForEnter=false; - { - for(int i=0; i< args.length; i++) { - final String arg = args[i]; - - if( arg.equals("-wait") ) { - waitForEnter = true; - } else if( arg.equals("-show_update_events") ) { - test.SHOW_UPDATE_EVENTS = true; - } else if( arg.equals("-quiet") ) { - test.QUIET = true; - } else if( arg.equals("-dev_id") && args.length > (i+1) ) { - test.dev_id = Integer.valueOf(args[++i]).intValue(); - } else if( arg.equals("-shutdown") && args.length > (i+1) ) { - test.shutdownTest = Integer.valueOf(args[++i]).intValue(); - } else if( arg.equals("-mac") && args.length > (i+1) ) { - test.waitForDevices.add(args[++i]); - } else if( arg.equals("-wl") && args.length > (i+1) ) { - final String addr = args[++i]; - println("Whitelist + "+addr); - test.whitelist.add(addr); - test.USE_WHITELIST = true; - } else if( arg.equals("-char") && args.length > (i+1) ) { - test.charIdentifierList.add(args[++i]); - } else if( arg.equals("-disconnect") ) { - test.KEEP_CONNECTED = false; - } else if( arg.equals("-enableGATTPing") ) { - test.GATT_PING_ENABLED = true; - } else if( arg.equals("-keepDevice") ) { - test.REMOVE_DEVICE = false; - } else if( arg.equals("-count") && args.length > (i+1) ) { - test.MULTI_MEASUREMENTS = Integer.valueOf(args[++i]).intValue(); - } else if( arg.equals("-single") ) { - test.MULTI_MEASUREMENTS = -1; - } - } - println("Run with '[-default_dev_id ] [-dev_id ] [-btmode LE|BREDR|DUAL] "+ - "[-bluetoothManager ] "+ - "[-disconnect] [-enableGATTPing] [-count ] [-single] (-char )* [-show_update_events] [-quiet]"+ - "(-mac )* (-wl )* "+ - "[-verbose] [-debug] "+ - "[-dbt_verbose true|false] "+ - "[-dbt_debug true|false|adapter.event,gatt.data,hci.event,mgmt.event] "+ - "[-dbt_mgmt cmd.timeout=3000,ringsize=64,...] "+ - "[-dbt_hci cmd.complete.timeout=10000,cmd.status.timeout=3000,ringsize=64,...] "+ - "[-dbt_gatt cmd.read.timeout=500,cmd.write.timeout=500,cmd.init.timeout=2500,ringsize=128,...] "+ - "[-dbt_l2cap reader.timeout=10000,restart.count=0,...] "+ - "[-shutdown ]'"); - } - - println("MULTI_MEASUREMENTS "+test.MULTI_MEASUREMENTS); - println("KEEP_CONNECTED "+test.KEEP_CONNECTED); - println("GATT_PING_ENABLED "+test.GATT_PING_ENABLED); - println("REMOVE_DEVICE "+test.REMOVE_DEVICE); - println("USE_WHITELIST "+test.USE_WHITELIST); - println("SHOW_UPDATE_EVENTS "+test.SHOW_UPDATE_EVENTS); - println("QUIET "+test.QUIET); - - println("dev_id "+test.dev_id); - println("waitForDevice: "+Arrays.toString(test.waitForDevices.toArray())); - println("characteristicList: "+Arrays.toString(test.charIdentifierList.toArray())); - - if( waitForEnter ) { - println("Press ENTER to continue\n"); - try{ System.in.read(); - } catch(final Exception e) { } - } - test.runTest(manager); - } - - static class BooleanNotification implements BluetoothNotification { - private final long t0; - private final String name; - private boolean v; - - public BooleanNotification(final String name, final long t0) { - this.t0 = t0; - this.name = name; - this.v = false; - } - - @Override - public void run(final Boolean v) { - synchronized(this) { - final long t1 = BluetoothUtils.getCurrentMilliseconds(); - this.v = v.booleanValue(); - System.out.println("###### "+name+": "+v+" in td "+(t1-t0)+" ms!"); - this.notifyAll(); - } - } - public boolean getValue() { - synchronized(this) { - return v; - } - } - } -} diff --git a/java/org/tinyb/BluetoothFactory.java b/java/org/tinyb/BluetoothFactory.java index 272a15ba..f47d44e0 100644 --- a/java/org/tinyb/BluetoothFactory.java +++ b/java/org/tinyb/BluetoothFactory.java @@ -508,7 +508,7 @@ public class BluetoothFactory { /* pp */ static long getStartupTimeMilliseconds() { return t0; } } -/** \example ScannerTinyB10.java +/** \example DBTScanner10.java * This Java scanner example uses the Direct-BT fully event driven workflow * and adds multithreading, i.e. one thread processes each found device found * as notified via the event listener. diff --git a/scripts/run-java-scanner10.sh b/scripts/run-java-scanner10.sh index 465644b4..4867238b 100644 --- a/scripts/run-java-scanner10.sh +++ b/scripts/run-java-scanner10.sh @@ -11,7 +11,7 @@ # ../scripts/run-java-scanner10.sh -wait 2>&1 | tee ~/scanner-h01-java10.log # -if [ ! -e lib/java/tinyb2.jar -o ! -e bin/java/ScannerTinyB10.jar -o ! -e lib/libdirect_bt.so ] ; then +if [ ! -e lib/java/tinyb2.jar -o ! -e bin/java/DBTScanner10.jar -o ! -e lib/libdirect_bt.so ] ; then echo run from dist directory exit 1 fi @@ -26,4 +26,4 @@ echo COMMANDLINE $0 $* echo direct_bt_debug $direct_bt_debug echo direct_bt_verbose $direct_bt_verbose -java -cp lib/java/tinyb2.jar:bin/java/ScannerTinyB10.jar -Djava.library.path=`pwd`/lib ScannerTinyB10 $* +java -cp lib/java/tinyb2.jar:bin/java/DBTScanner10.jar -Djava.library.path=`pwd`/lib DBTScanner10 $* -- cgit v1.2.3