diff options
-rw-r--r-- | examples/java/ScannerTinyB01.java | 46 | ||||
-rw-r--r-- | examples/java/ScannerTinyB10.java | 103 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTAdapter.java | 90 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTDevice.java | 124 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTGattCharacteristic.java | 56 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTGattService.java | 68 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTManager.java | 139 |
7 files changed, 561 insertions, 65 deletions
diff --git a/examples/java/ScannerTinyB01.java b/examples/java/ScannerTinyB01.java index e807b58d..c2f7a739 100644 --- a/examples/java/ScannerTinyB01.java +++ b/examples/java/ScannerTinyB01.java @@ -24,6 +24,7 @@ */ import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -38,6 +39,7 @@ import org.tinyb.BluetoothGattCharacteristic; 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; @@ -55,6 +57,7 @@ public class ScannerTinyB01 { static final String EUI48_ANY_DEVICE = "00:00:00:00:00:00"; static String waitForDevice = EUI48_ANY_DEVICE; + static List<String> characteristicList = new ArrayList<String>(); public static void main(final String[] args) throws InterruptedException { final boolean waitForEnter=false; @@ -72,6 +75,8 @@ public class ScannerTinyB01 { dev_id = Integer.valueOf(args[++i]).intValue(); } else if( arg.equals("-mac") && args.length > (i+1) ) { waitForDevice = args[++i]; + } else if( arg.equals("-char") && args.length > (i+1) ) { + characteristicList.add(args[++i]); } else if( arg.equals("-mode") && args.length > (i+1) ) { mode = Integer.valueOf(args[++i]).intValue(); } else if( arg.equals("-bluetoothManager") && args.length > (i+1) ) { @@ -85,12 +90,13 @@ public class ScannerTinyB01 { } } - System.err.println("Run with '[-dev_id <adapter-index>] [-mac <device_address>] [-mode <mode>] [-bluetoothManager <BluetoothManager-Implementation-Class-Name>]'"); + System.err.println("Run with '[-dev_id <adapter-index>] [-mac <device_address>] (-char <uuid>)* [-mode <mode>] [-bluetoothManager <BluetoothManager-Implementation-Class-Name>]'"); } System.err.println("BluetoothManager "+bluetoothManagerClazzName); System.err.println("dev_id "+dev_id); System.err.println("waitForDevice: "+waitForDevice); + System.err.println("characteristicList: "+Arrays.toString(characteristicList.toArray())); System.err.println("mode "+mode); if( waitForEnter ) { @@ -241,7 +247,15 @@ public class ScannerTinyB01 { matchingDiscoveredDeviceBucket[0] = null; } } else if( 1 == mode ) { - sensor = adapter.find(null, waitForDevice, t0_discovery); + boolean timeout = false; + while( null == sensor && !timeout ) { + sensor = adapter.find(null, waitForDevice, t0_discovery); + if( null == sensor ) { + final long tn = BluetoothUtils.getCurrentMilliseconds(); + timeout = ( tn - t0 ) > t0_discovery; + Thread.sleep(60); + } + } } else { boolean timeout = false; while( null == sensor && !timeout ) { @@ -310,6 +324,21 @@ public class ScannerTinyB01 { if ( null == primServices || primServices.isEmpty() ) { System.err.println("No BluetoothGattService found!"); } else { + { + for(final String characteristic : characteristicList) { + final BluetoothGattCharacteristic char0 = (BluetoothGattCharacteristic) + manager.find(BluetoothType.GATT_CHARACTERISTIC, null, characteristic, null, 1000); + final BluetoothGattCharacteristic char1 = (BluetoothGattCharacteristic) + manager.find(BluetoothType.GATT_CHARACTERISTIC, null, characteristic, sensor.getAdapter(), 1000); + final BluetoothGattCharacteristic char2 = (BluetoothGattCharacteristic) + manager.find(BluetoothType.GATT_CHARACTERISTIC, null, characteristic, sensor, 1000); + System.err.println("Char UUID "+characteristic); + //System.err.println(" over manager: "+char0); + System.err.println(" over adapter: "+char1); + System.err.println(" over device : "+char2); + } + } + final boolean addedCharacteristicListenerRes; if( isDirectBT ) { addedCharacteristicListenerRes = @@ -330,10 +359,15 @@ public class ScannerTinyB01 { System.err.printf(" [%02d.%02d] Decla: %s\n", i, j, serviceChar.toString()); final List<String> properties = Arrays.asList(serviceChar.getFlags()); if( properties.contains("read") ) { - final byte[] value = serviceChar.readValue(); - final String svalue = BluetoothUtils.decodeUTF8String(value, 0, value.length); - System.err.printf(" [%02d.%02d] Value: %s ('%s')\n", - i, j, BluetoothUtils.bytesHexString(value, true, true), svalue); + try { + final byte[] value = serviceChar.readValue(); + final String svalue = BluetoothUtils.decodeUTF8String(value, 0, value.length); + System.err.printf(" [%02d.%02d] Value: %s ('%s')\n", + i, j, BluetoothUtils.bytesHexString(value, true, true), svalue); + } catch( final Exception ex) { + System.err.println("Caught "+ex.getMessage()); + ex.printStackTrace(); + } } } } diff --git a/examples/java/ScannerTinyB10.java b/examples/java/ScannerTinyB10.java index 1d0c35c4..8d3d2b10 100644 --- a/examples/java/ScannerTinyB10.java +++ b/examples/java/ScannerTinyB10.java @@ -43,6 +43,7 @@ import org.tinyb.BluetoothGattCharacteristic; 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; @@ -53,6 +54,8 @@ public class ScannerTinyB10 { static final String EUI48_ANY_DEVICE = "00:00:00:00:00:00"; final List<String> waitForDevices = new ArrayList<String>(); + final boolean isDirectBT; + final BluetoothManager manager; long timestamp_t0; @@ -61,11 +64,10 @@ public class ScannerTinyB10 { boolean REMOVE_DEVICE = true; boolean USE_WHITELIST = false; final List<String> whitelist = new ArrayList<String>(); + final List<String> characteristicList = new ArrayList<String>(); boolean SHOW_UPDATE_EVENTS = false; - String bluetoothManagerClazzName = BluetoothFactory.DirectBTImplementationID.BluetoothManagerClassName; - int dev_id = 0; // default Collection<String> devicesInProcessing = Collections.synchronizedCollection(new ArrayList<>()); @@ -221,6 +223,20 @@ public class ScannerTinyB10 { "PERF: discovered to gatt-complete " + tdc5 + " ms (connect " + (tdc5 - td15) + " ms),"+System.lineSeparator()+ "PERF: adapter-init to gatt-complete " + td05 + " ms"+System.lineSeparator()); } + { + for(final String characteristic : characteristicList) { + final BluetoothGattCharacteristic char0 = (BluetoothGattCharacteristic) + manager.find(BluetoothType.GATT_CHARACTERISTIC, null, characteristic, null); + final BluetoothGattCharacteristic char1 = (BluetoothGattCharacteristic) + manager.find(BluetoothType.GATT_CHARACTERISTIC, null, characteristic, device.getAdapter()); + final BluetoothGattCharacteristic char2 = (BluetoothGattCharacteristic) + manager.find(BluetoothType.GATT_CHARACTERISTIC, null, characteristic, device); + System.err.println("Char UUID "+characteristic); + System.err.println(" over manager: "+char0); + System.err.println(" over adapter: "+char1); + System.err.println(" over device : "+char2); + } + } final boolean addedCharacteristicListenerRes = BluetoothGattService.addCharacteristicListenerToAll(device, primServices, myCharacteristicListener); System.err.println("Added GATTCharacteristicListener: "+addedCharacteristicListenerRes); @@ -236,10 +252,15 @@ public class ScannerTinyB10 { System.err.printf(" [%02d.%02d] Decla: %s\n", i, j, serviceChar.toString()); final List<String> properties = Arrays.asList(serviceChar.getFlags()); if( properties.contains("read") ) { - final byte[] value = serviceChar.readValue(); - final String svalue = BluetoothUtils.decodeUTF8String(value, 0, value.length); - System.err.printf(" [%02d.%02d] Value: %s ('%s')\n", - i, j, BluetoothUtils.bytesHexString(value, true, true), svalue); + try { + final byte[] value = serviceChar.readValue(); + final String svalue = BluetoothUtils.decodeUTF8String(value, 0, value.length); + System.err.printf(" [%02d.%02d] Value: %s ('%s')\n", + i, j, BluetoothUtils.bytesHexString(value, true, true), svalue); + } catch( final Exception ex) { + System.err.println("Caught "+ex.getMessage()); + ex.printStackTrace(); + } } } } @@ -295,30 +316,29 @@ public class ScannerTinyB10 { } } - public void runTest() { - final boolean isDirectBT; - final BluetoothManager manager; - { - BluetoothManager _manager = null; - final BluetoothFactory.ImplementationIdentifier implID = BluetoothFactory.getImplementationIdentifier(bluetoothManagerClazzName); - if( null == implID ) { - System.err.println("Unable to find BluetoothManager "+bluetoothManagerClazzName); - System.exit(-1); - } - isDirectBT = BluetoothFactory.DirectBTImplementationID.equals(implID); - System.err.println("Using BluetoothManager "+bluetoothManagerClazzName); - System.err.println("Using Implementation "+implID+", isDirectBT "+isDirectBT); - try { - _manager = BluetoothFactory.getBluetoothManager( implID ); - } catch (BluetoothException | NoSuchMethodException | SecurityException - | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | ClassNotFoundException e) { - System.err.println("Unable to instantiate BluetoothManager via "+implID); - e.printStackTrace(); - System.exit(-1); - } - manager = _manager; + public ScannerTinyB10(final String bluetoothManagerClazzName) { + BluetoothManager _manager = null; + final BluetoothFactory.ImplementationIdentifier implID = BluetoothFactory.getImplementationIdentifier(bluetoothManagerClazzName); + if( null == implID ) { + System.err.println("Unable to find BluetoothManager "+bluetoothManagerClazzName); + System.exit(-1); } + isDirectBT = BluetoothFactory.DirectBTImplementationID.equals(implID); + System.err.println("Using BluetoothManager "+bluetoothManagerClazzName); + System.err.println("Using Implementation "+implID+", isDirectBT "+isDirectBT); + try { + _manager = BluetoothFactory.getBluetoothManager( implID ); + } catch (BluetoothException | NoSuchMethodException | SecurityException + | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | ClassNotFoundException e) { + System.err.println("Unable to instantiate BluetoothManager via "+implID); + e.printStackTrace(); + System.exit(-1); + } + manager = _manager; + } + + public void runTest() { final BluetoothAdapter adapter; { final List<BluetoothAdapter> adapters = manager.getAdapters(); @@ -382,7 +402,17 @@ public class ScannerTinyB10 { } public static void main(final String[] args) throws InterruptedException { - final ScannerTinyB10 test = new ScannerTinyB10(); + String bluetoothManagerClazzName = BluetoothFactory.DirectBTImplementationID.BluetoothManagerClassName; + for(int i=0; i< args.length; i++) { + final String arg = args[i]; + if( arg.equals("-bluetoothManager") && args.length > (i+1) ) { + bluetoothManagerClazzName = args[++i]; + } else if( arg.equals("-debug") ) { + System.setProperty("org.tinyb.verbose", "true"); + System.setProperty("org.tinyb.debug", "true"); + } + } + final ScannerTinyB10 test = new ScannerTinyB10(bluetoothManagerClazzName); boolean waitForEnter=false; { @@ -402,6 +432,8 @@ public class ScannerTinyB10 { System.err.println("Whitelist + "+addr); test.whitelist.add(addr); test.USE_WHITELIST = true; + } else if( arg.equals("-char") && args.length > (i+1) ) { + test.characteristicList.add(args[++i]); } else if( arg.equals("-disconnect") ) { test.KEEP_CONNECTED = false; } else if( arg.equals("-keepDevice") ) { @@ -410,24 +442,19 @@ public class ScannerTinyB10 { test.MULTI_MEASUREMENTS = Integer.valueOf(args[++i]).intValue(); } else if( arg.equals("-single") ) { test.MULTI_MEASUREMENTS = -1; - } else if( arg.equals("-debug") ) { - System.setProperty("org.tinyb.verbose", "true"); - System.setProperty("org.tinyb.debug", "true"); - } else if( arg.equals("-bluetoothManager") && args.length > (i+1) ) { - test.bluetoothManagerClazzName = args[++i]; } } - - System.err.println("Run with '[-dev_id <adapter-index>] (-mac <device_address>)* [-disconnect] [-count <number>] [-single] (-wl <device_address>)* [-show_update_events] [-bluetoothManager <BluetoothManager-Implementation-Class-Name>]'"); + System.err.println("Run with '[-dev_id <adapter-index>] (-mac <device_address>)* [-disconnect] [-count <number>] [-single] (-wl <device_address>)* (-char <uuid>)* [-show_update_events] [-bluetoothManager <BluetoothManager-Implementation-Class-Name>]'"); } - System.err.println("BluetoothManager "+test.bluetoothManagerClazzName); + System.err.println("BluetoothManager "+bluetoothManagerClazzName); System.err.println("MULTI_MEASUREMENTS "+test.MULTI_MEASUREMENTS); System.err.println("KEEP_CONNECTED "+test.KEEP_CONNECTED); System.err.println("REMOVE_DEVICE "+test.REMOVE_DEVICE); System.err.println("USE_WHITELIST "+test.USE_WHITELIST); System.err.println("dev_id "+test.dev_id); System.err.println("waitForDevice: "+Arrays.toString(test.waitForDevices.toArray())); + System.err.println("characteristicList: "+Arrays.toString(test.characteristicList.toArray())); if( waitForEnter ) { System.err.println("Press ENTER to continue\n"); diff --git a/java/direct_bt/tinyb/DBTAdapter.java b/java/direct_bt/tinyb/DBTAdapter.java index 4c924b6d..aa49a3f1 100644 --- a/java/direct_bt/tinyb/DBTAdapter.java +++ b/java/direct_bt/tinyb/DBTAdapter.java @@ -38,8 +38,12 @@ import org.tinyb.BluetoothAdapter; import org.tinyb.BluetoothAddressType; import org.tinyb.BluetoothDevice; import org.tinyb.BluetoothException; +import org.tinyb.BluetoothGattCharacteristic; +import org.tinyb.BluetoothGattDescriptor; +import org.tinyb.BluetoothGattService; import org.tinyb.BluetoothManager; import org.tinyb.BluetoothNotification; +import org.tinyb.BluetoothObject; import org.tinyb.BluetoothType; import org.tinyb.EIRDataTypeSet; import org.tinyb.HCIStatusCode; @@ -129,8 +133,7 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter @Override public BluetoothDevice find(final String name, final String address, final long timeoutMS) { - final BluetoothManager manager = DBTManager.getBluetoothManager(); - return (BluetoothDevice) manager.find(BluetoothType.DEVICE, name, address, this, timeoutMS); + return (DBTDevice) findInCache(name, address, BluetoothType.DEVICE); } @Override @@ -489,4 +492,87 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter } } }; + + /** + * Returns the matching {@link DBTObject} from the internal cache if found, + * otherwise {@code null}. + * <p> + * The returned {@link DBTObject} may be of type + * <ul> + * <li>{@link DBTDevice}</li> + * <li>{@link DBTGattService}</li> + * <li>{@link DBTGattCharacteristic}</li> + * <li>{@link DBTGattDescriptor}</li> + * </ul> + * or alternatively in {@link BluetoothObject} space + * <ul> + * <li>{@link BluetoothType#DEVICE} -> {@link BluetoothDevice}</li> + * <li>{@link BluetoothType#GATT_SERVICE} -> {@link BluetoothGattService}</li> + * <li>{@link BluetoothType#GATT_CHARACTERISTIC} -> {@link BluetoothGattCharacteristic}</li> + * <li>{@link BluetoothType#GATT_DESCRIPTOR} -> {@link BluetoothGattDescriptor}</li> + * </ul> + * </p> + * @param name name of the desired {@link BluetoothType#DEVICE device}. + * Maybe {@code null}. + * @param identifier EUI48 address of the desired {@link BluetoothType#DEVICE device} + * or UUID of the desired {@link BluetoothType#GATT_SERVICE service}, + * {@link BluetoothType#GATT_CHARACTERISTIC characteristic} or {@link BluetoothType#GATT_DESCRIPTOR descriptor} to be found. + * Maybe {@code null}, in which case the first object of the desired type is being returned - if existing. + * @param type specify the type of the object to be found, either + * {@link BluetoothType#DEVICE device}, + * {@link BluetoothType#GATT_SERVICE service}, {@link BluetoothType#GATT_CHARACTERISTIC characteristic} + * or {@link BluetoothType#GATT_DESCRIPTOR descriptor}. + * {@link BluetoothType#NONE none} means anything. + */ + /* pp */ DBTObject findInCache(final String name, final String identifier, final BluetoothType type) { + final boolean anyType = BluetoothType.NONE == type; + final boolean deviceType = BluetoothType.DEVICE == type; + final boolean serviceType = BluetoothType.GATT_SERVICE == type; + final boolean charType = BluetoothType.GATT_CHARACTERISTIC== type; + final boolean descType = BluetoothType.GATT_DESCRIPTOR == type; + + if( !anyType && !deviceType && !serviceType && !charType && !descType ) { + return null; + } + synchronized(discoveredDevicesLock) { + if( null == name && null == identifier && ( anyType || deviceType ) ) { + // special case for 1st valid device + if( discoveredDevices.size() > 0 ) { + return (DBTDevice) discoveredDevices.get(0); + } + return null; // no device + } + for(int devIdx = discoveredDevices.size() - 1; devIdx >= 0; devIdx-- ) { + final DBTDevice device = (DBTDevice) discoveredDevices.get(devIdx); + if( ( anyType || deviceType ) ) { + if( null != name && null != identifier && + device.getName().equals(name) && + device.getAddress().equals(identifier) + ) + { + return device; + } + if( null != identifier && + device.getAddress().equals(identifier) + ) + { + return device; + } + if( null != name && + device.getName().equals(name) + ) + { + return device; + } + } + if( anyType || serviceType || charType || descType ) { + final DBTObject dbtObj = device.findInCache(identifier, type); + if( null != dbtObj ) { + return dbtObj; + } + } + } + return null; + } + } } diff --git a/java/direct_bt/tinyb/DBTDevice.java b/java/direct_bt/tinyb/DBTDevice.java index 74547cef..a98636bb 100644 --- a/java/direct_bt/tinyb/DBTDevice.java +++ b/java/direct_bt/tinyb/DBTDevice.java @@ -26,6 +26,7 @@ package direct_bt.tinyb; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -36,9 +37,11 @@ import org.tinyb.BluetoothAddressType; import org.tinyb.BluetoothDevice; import org.tinyb.BluetoothException; import org.tinyb.BluetoothGattCharacteristic; +import org.tinyb.BluetoothGattDescriptor; import org.tinyb.BluetoothGattService; import org.tinyb.BluetoothManager; import org.tinyb.BluetoothNotification; +import org.tinyb.BluetoothObject; import org.tinyb.BluetoothType; import org.tinyb.EIRDataTypeSet; import org.tinyb.GATTCharacteristicListener; @@ -58,6 +61,7 @@ public class DBTDevice extends DBTObject implements BluetoothDevice private volatile String name; long ts_last_discovery; long ts_last_update; + /* pp */ final List<WeakReference<DBTGattService>> serviceCache = new ArrayList<WeakReference<DBTGattService>>(); private final Object userCallbackLock = new Object(); @@ -127,6 +131,7 @@ public class DBTDevice extends DBTObject implements BluetoothDevice @Override public void deviceDisconnected(final BluetoothDevice device, final HCIStatusCode reason, final long timestamp) { if( isConnected.compareAndSet(true, false) ) { + clearServiceCache(); synchronized(userCallbackLock) { if( servicesResolved.compareAndSet(true, false) ) { if( null != userServicesResolvedNotificationsCB ) { @@ -246,6 +251,8 @@ public class DBTDevice extends DBTObject implements BluetoothDevice disableTrustedNotifications(); // FIXME disableTrustedNotificationsImpl(); + clearServiceCache(); + final DBTAdapter a = getAdapter(); if( null != a ) { a.removeStatusListener(statusListener); @@ -297,9 +304,7 @@ public class DBTDevice extends DBTObject implements BluetoothDevice @Override public BluetoothGattService find(final String UUID, final long timeoutMS) { - final BluetoothManager manager = DBTManager.getBluetoothManager(); - return (BluetoothGattService) manager.find(BluetoothType.GATT_SERVICE, - null, UUID, this, timeoutMS); + return (DBTGattService) findInCache(UUID, BluetoothType.GATT_SERVICE); } @Override @@ -583,7 +588,9 @@ public class DBTDevice extends DBTObject implements BluetoothDevice @Override public List<BluetoothGattService> getServices() { try { - return getServicesImpl(); + final List<BluetoothGattService> services = getServicesImpl(); + updateServiceCache(services); + return services; } catch (final Throwable t) { if(DEBUG) { System.err.println("Caught "+t.getMessage()+" on thread "+Thread.currentThread().toString()); @@ -645,4 +652,113 @@ public class DBTDevice extends DBTObject implements BluetoothDevice @Override public native int removeAllCharacteristicListener(); + + /* local functionality */ + + private void clearServiceCache() { + synchronized(serviceCache) { + for(int i = serviceCache.size() - 1; i >= 0; i-- ) { + serviceCache.get(i).clear(); + serviceCache.remove(i); + } + } + } + private void updateServiceCache(final List<BluetoothGattService> services) { + synchronized(serviceCache) { + clearServiceCache(); + if( null != services ) { + for(final BluetoothGattService service : services) { + serviceCache.add( new WeakReference<DBTGattService>( (DBTGattService)service ) ); + } + } + } + } + + /* pp */ boolean checkServiceCache(final boolean getServices) { + synchronized(serviceCache) { + if( serviceCache.isEmpty() ) { + if( getServices ) { + getServices(); + if( serviceCache.isEmpty() ) { + return false; + } + } else { + return false; + } + } + return true; + } + } + + /** + * Returns the matching {@link DBTObject} from the internal cache if found, + * otherwise {@code null}. + * <p> + * The returned {@link DBTObject} may be of type + * <ul> + * <li>{@link DBTGattService}</li> + * <li>{@link DBTGattCharacteristic}</li> + * <li>{@link DBTGattDescriptor}</li> + * </ul> + * or alternatively in {@link BluetoothObject} space + * <ul> + * <li>{@link BluetoothType#GATT_SERVICE} -> {@link BluetoothGattService}</li> + * <li>{@link BluetoothType#GATT_CHARACTERISTIC} -> {@link BluetoothGattCharacteristic}</li> + * <li>{@link BluetoothType#GATT_DESCRIPTOR} -> {@link BluetoothGattDescriptor}</li> + * </ul> + * </p> + * @param uuid UUID of the desired {@link BluetoothType#GATT_SERVICE service}, + * {@link BluetoothType#GATT_CHARACTERISTIC characteristic} or {@link BluetoothType#GATT_DESCRIPTOR descriptor} to be found. + * Maybe {@code null}, in which case the first object of the desired type is being returned - if existing. + * @param type specify the type of the object to be found, either + * {@link BluetoothType#GATT_SERVICE service}, {@link BluetoothType#GATT_CHARACTERISTIC characteristic} + * or {@link BluetoothType#GATT_DESCRIPTOR descriptor}. + * {@link BluetoothType#NONE none} means anything. + */ + /* pp */ DBTObject findInCache(final String uuid, final BluetoothType type) { + final boolean anyType = BluetoothType.NONE == type; + final boolean serviceType = BluetoothType.GATT_SERVICE == type; + final boolean charType = BluetoothType.GATT_CHARACTERISTIC== type; + final boolean descType = BluetoothType.GATT_DESCRIPTOR == type; + + if( !anyType && !serviceType && !charType && !descType ) { + return null; + } + synchronized(serviceCache) { + if( !checkServiceCache(true) ) { + return null; + } + + if( null == uuid && ( anyType || serviceType ) ) { + // special case for 1st valid service ref + while( !serviceCache.isEmpty() ) { + final DBTGattService service = serviceCache.get(0).get(); + if( null == service ) { + serviceCache.remove(0); + } else { + return service; + } + } + return null; // empty valid service refs + } + for(int srvIdx = serviceCache.size() - 1; srvIdx >= 0; srvIdx-- ) { + final DBTGattService service = serviceCache.get(srvIdx).get(); + if( null == service ) { + serviceCache.remove(srvIdx); // remove dead ref + continue; // cont w/ next service + } + if( ( anyType || serviceType ) && service.getUUID().equals(uuid) ) { + return service; + } + if( anyType || charType || descType ) { + final DBTObject dbtObj = service.findInCache(uuid, type); + if( null != dbtObj ) { + return dbtObj; + } + } + } + return null; + } + } + } diff --git a/java/direct_bt/tinyb/DBTGattCharacteristic.java b/java/direct_bt/tinyb/DBTGattCharacteristic.java index 272735e0..0233eff9 100644 --- a/java/direct_bt/tinyb/DBTGattCharacteristic.java +++ b/java/direct_bt/tinyb/DBTGattCharacteristic.java @@ -35,6 +35,7 @@ import org.tinyb.BluetoothGattDescriptor; import org.tinyb.BluetoothGattService; import org.tinyb.BluetoothManager; import org.tinyb.BluetoothNotification; +import org.tinyb.BluetoothObject; import org.tinyb.BluetoothType; import org.tinyb.BluetoothUtils; import org.tinyb.GATTCharacteristicListener; @@ -73,7 +74,7 @@ public class DBTGattCharacteristic extends DBTObject implements BluetoothGattCha /* Optional Client Characteristic Configuration index within descriptorList */ private final int clientCharacteristicsConfigIndex; - private final List<BluetoothGattDescriptor> descriptorList; + /* pp */ final List<BluetoothGattDescriptor> descriptorList; private byte[] cachedValue = null; private BluetoothNotification<byte[]> valueNotificationCB = null; @@ -194,9 +195,10 @@ public class DBTGattCharacteristic extends DBTObject implements BluetoothGattCha @Override public BluetoothGattDescriptor find(final String UUID, final long timeoutMS) { - final BluetoothManager manager = DBTManager.getBluetoothManager(); - return (BluetoothGattDescriptor) manager.find(BluetoothType.GATT_DESCRIPTOR, - null, UUID, this, timeoutMS); + if( !checkServiceCache() ) { + return null; + } + return (DBTGattDescriptor) findInCache(UUID, BluetoothType.GATT_DESCRIPTOR); } @Override @@ -331,4 +333,50 @@ public class DBTGattCharacteristic extends DBTObject implements BluetoothGattCha @Override protected native void deleteImpl(long nativeInstance); + /* local functionality */ + + /* pp */ boolean checkServiceCache() { + final DBTGattService service = wbr_service.get(); + if( null == service ) { + return false; + } + final DBTDevice device = service.wbr_device.get(); + return null != device && device.checkServiceCache(false); + } + + /** + * Returns the matching {@link DBTObject} from the internal cache if found, + * otherwise {@code null}. + * <p> + * The returned {@link DBTObject} may be of type + * <ul> + * <li>{@link DBTGattDescriptor}</li> + * </ul> + * or alternatively in {@link BluetoothObject} space + * <ul> + * <li>{@link BluetoothType#GATT_DESCRIPTOR} -> {@link BluetoothGattDescriptor}</li> + * </ul> + * </p> + * @param uuid UUID of the desired + * {@link BluetoothType#GATT_DESCRIPTOR descriptor} to be found. + * Maybe {@code null}, in which case the first object of the desired type is being returned - if existing. + * @param type specify the type of the object to be found, a {@link BluetoothType#GATT_DESCRIPTOR descriptor}. + * {@link BluetoothType#NONE none} means anything. + */ + /* pp */ DBTObject findInCache(final String uuid, final BluetoothType type) { + final boolean anyType = BluetoothType.NONE == type; + final boolean descType = BluetoothType.GATT_DESCRIPTOR == type; + + if( !anyType && !descType ) { + return null; + } + final int size = descriptorList.size(); + for(int i = 0; i < size; i++ ) { + final DBTGattDescriptor descr = (DBTGattDescriptor) descriptorList.get(i); + if( null == uuid || descr.getUUID().equals(uuid) ) { + return descr; + } + } + return null; + } } diff --git a/java/direct_bt/tinyb/DBTGattService.java b/java/direct_bt/tinyb/DBTGattService.java index 98081e25..4dbcc71e 100644 --- a/java/direct_bt/tinyb/DBTGattService.java +++ b/java/direct_bt/tinyb/DBTGattService.java @@ -30,8 +30,10 @@ import java.util.List; import org.tinyb.BluetoothDevice; import org.tinyb.BluetoothGattCharacteristic; +import org.tinyb.BluetoothGattDescriptor; import org.tinyb.BluetoothGattService; import org.tinyb.BluetoothManager; +import org.tinyb.BluetoothObject; import org.tinyb.BluetoothType; public class DBTGattService extends DBTObject implements BluetoothGattService @@ -43,7 +45,7 @@ public class DBTGattService extends DBTObject implements BluetoothGattService private final String type_uuid; private final short handleStart; private final short handleEnd; - private final List<BluetoothGattCharacteristic> characteristicList; + /* pp */ final List<BluetoothGattCharacteristic> characteristicList; /* pp */ DBTGattService(final long nativeInstance, final DBTDevice device, final boolean isPrimary, final String type_uuid, final short handleStart, final short handleEnd) @@ -81,14 +83,15 @@ public class DBTGattService extends DBTObject implements BluetoothGattService @Override public BluetoothGattCharacteristic find(final String UUID, final long timeoutMS) { - final BluetoothManager manager = DBTManager.getBluetoothManager(); - return (DBTGattCharacteristic) manager.find(BluetoothType.GATT_CHARACTERISTIC, - null, UUID, this, timeoutMS); + if( !checkServiceCache() ) { + return null; + } + return (DBTGattCharacteristic) findInCache(UUID, BluetoothType.GATT_CHARACTERISTIC); } @Override public BluetoothGattCharacteristic find(final String UUID) { - return find(UUID, 0); + return find(UUID, 0); } @Override @@ -132,4 +135,59 @@ public class DBTGattService extends DBTObject implements BluetoothGattService @Override protected native void deleteImpl(long nativeInstance); + + /* local functionality */ + + /* pp */ boolean checkServiceCache() { + final DBTDevice device = wbr_device.get(); + return null != device && device.checkServiceCache(false); + } + + /** + * Returns the matching {@link DBTObject} from the internal cache if found, + * otherwise {@code null}. + * <p> + * The returned {@link DBTObject} may be of type + * <ul> + * <li>{@link DBTGattCharacteristic}</li> + * <li>{@link DBTGattDescriptor}</li> + * </ul> + * or alternatively in {@link BluetoothObject} space + * <ul> + * <li>{@link BluetoothType#GATT_CHARACTERISTIC} -> {@link BluetoothGattCharacteristic}</li> + * <li>{@link BluetoothType#GATT_DESCRIPTOR} -> {@link BluetoothGattDescriptor}</li> + * </ul> + * </p> + * @param uuid UUID of the desired + * {@link BluetoothType#GATT_CHARACTERISTIC characteristic} or {@link BluetoothType#GATT_DESCRIPTOR descriptor} to be found. + * Maybe {@code null}, in which case the first object of the desired type is being returned - if existing. + * @param type specify the type of the object to be found, either + * {@link BluetoothType#GATT_CHARACTERISTIC characteristic} + * or {@link BluetoothType#GATT_DESCRIPTOR descriptor}. + * {@link BluetoothType#NONE none} means anything. + */ + /* pp */ DBTObject findInCache(final String uuid, final BluetoothType type) { + final boolean anyType = BluetoothType.NONE == type; + final boolean charType = BluetoothType.GATT_CHARACTERISTIC== type; + final boolean descType = BluetoothType.GATT_DESCRIPTOR == type; + + if( !anyType && !charType && !descType ) { + return null; + } + final int characteristicSize = characteristicList.size(); + for(int charIdx = 0; charIdx < characteristicSize; charIdx++ ) { + final DBTGattCharacteristic characteristic = (DBTGattCharacteristic) characteristicList.get(charIdx); + if( ( anyType || charType ) && ( null == uuid || characteristic.getUUID().equals(uuid) ) ) { + return characteristic; + } + if( anyType || descType ) { + final DBTObject dbtObj = characteristic.findInCache(uuid, type); + if( null != dbtObj ) { + return dbtObj; + } + } + } + return null; + } + } diff --git a/java/direct_bt/tinyb/DBTManager.java b/java/direct_bt/tinyb/DBTManager.java index b9d99a4c..8dee59a5 100644 --- a/java/direct_bt/tinyb/DBTManager.java +++ b/java/direct_bt/tinyb/DBTManager.java @@ -35,6 +35,8 @@ import org.tinyb.BluetoothAdapter; import org.tinyb.BluetoothDevice; import org.tinyb.BluetoothException; import org.tinyb.BluetoothFactory; +import org.tinyb.BluetoothGattCharacteristic; +import org.tinyb.BluetoothGattDescriptor; import org.tinyb.BluetoothGattService; import org.tinyb.BluetoothObject; import org.tinyb.BluetoothManager; @@ -158,12 +160,9 @@ public class DBTManager implements BluetoothManager public BluetoothType getBluetoothType() { return BluetoothType.NONE; } - private DBTObject find(final int type, final String name, final String identifier, final BluetoothObject parent, final long milliseconds) - { throw new UnsupportedOperationException(); } // FIXME - @Override public DBTObject find(final BluetoothType type, final String name, final String identifier, final BluetoothObject parent, final long timeoutMS) { - return find(type.ordinal(), name, identifier, parent, timeoutMS); + return findInCache((DBTObject)parent, type, name, identifier); } @Override @@ -174,13 +173,19 @@ public class DBTManager implements BluetoothManager @SuppressWarnings("unchecked") @Override public <T extends BluetoothObject> T find(final String name, final String identifier, final BluetoothObject parent, final long timeoutMS) { - return (T) find(DBTObject.class_type().ordinal(), name, identifier, parent, timeoutMS); + // Due to generic type erasure, we cannot determine the matching BluetoothType for the return parameter, + // hence this orig TinyB API method is rather misleading than useful. + throw new UnsupportedOperationException("Generic return type 'find' won't be implemented."); + // return (T) find(BluetoothType.NONE, name, identifier, parent, timeoutMS); } @SuppressWarnings("unchecked") @Override public <T extends BluetoothObject> T find(final String name, final String identifier, final BluetoothObject parent) { - return (T) find(name, identifier, parent, 0); + // Due to generic type erasure, we cannot determine the matching BluetoothType for the return parameter, + // hence this orig TinyB API method is rather misleading than useful. + throw new UnsupportedOperationException("Generic return type 'find' won't be implemented."); + // return (T) find(BluetoothType.NONE, name, identifier, parent, 0); } @Override @@ -317,4 +322,126 @@ public class DBTManager implements BluetoothManager deleteImpl(nativeInstance); } + /** + * Returns the matching {@link DBTObject} from the internal cache if found, + * otherwise {@code null}. + * <p> + * The returned {@link DBTObject} may be of type + * <ul> + * <li>{@link DBTAdapter}</li> + * <li>{@link DBTDevice}</li> + * <li>{@link DBTGattService}</li> + * <li>{@link DBTGattCharacteristic}</li> + * <li>{@link DBTGattDescriptor}</li> + * </ul> + * or alternatively in {@link BluetoothObject} space + * <ul> + * <li>{@link BluetoothType#ADAPTER} -> {@link BluetoothAdapter}</li> + * <li>{@link BluetoothType#DEVICE} -> {@link BluetoothDevice}</li> + * <li>{@link BluetoothType#GATT_SERVICE} -> {@link BluetoothGattService}</li> + * <li>{@link BluetoothType#GATT_CHARACTERISTIC} -> {@link BluetoothGattCharacteristic}</li> + * <li>{@link BluetoothType#GATT_DESCRIPTOR} -> {@link BluetoothGattDescriptor}</li> + * </ul> + * </p> + * @param name name of the desired {@link BluetoothType#ADAPTER adapter} or {@link BluetoothType#DEVICE device}. + * Maybe {@code null}. + * @param identifier EUI48 address of the desired {@link BluetoothType#ADAPTER adapter} or {@link BluetoothType#DEVICE device} + * or UUID of the desired {@link BluetoothType#GATT_SERVICE service}, + * {@link BluetoothType#GATT_CHARACTERISTIC characteristic} or {@link BluetoothType#GATT_DESCRIPTOR descriptor} to be found. + * Maybe {@code null}, in which case the first object of the desired type is being returned - if existing. + * @param type specify the type of the object to be found, either + * {@link BluetoothType#ADAPTER adapter}, {@link BluetoothType#DEVICE device}, + * {@link BluetoothType#GATT_SERVICE service}, {@link BluetoothType#GATT_CHARACTERISTIC characteristic} + * or {@link BluetoothType#GATT_DESCRIPTOR descriptor}. + * {@link BluetoothType#NONE none} means anything. + */ + /* pp */ DBTObject findInCache(final String name, final String identifier, final BluetoothType type) { + final boolean anyType = BluetoothType.NONE == type; + final boolean adapterType = BluetoothType.ADAPTER == type; + + if( null == name && null == identifier && ( anyType || adapterType ) ) { + // special case for 1st valid adapter + if( adapters.size() > 0 ) { + return (DBTAdapter) adapters.get(0); + } + return null; // no adapter + } + for(int devIdx = adapters.size() - 1; devIdx >= 0; devIdx-- ) { + final DBTAdapter adapter = (DBTAdapter) adapters.get(devIdx); + if( ( anyType || adapterType ) ) { + if( null != name && null != identifier && + adapter.getName().equals(name) && + adapter.getAddress().equals(identifier) + ) + { + return adapter; + } + if( null != identifier && + adapter.getAddress().equals(identifier) + ) + { + return adapter; + } + if( null != name && + adapter.getName().equals(name) + ) + { + return adapter; + } + } + final DBTObject dbtObj = adapter.findInCache(name, identifier, type); + if( null != dbtObj ) { + return dbtObj; + } + } + return null; + } + + /* pp */ DBTObject findInCache(final DBTObject parent, final BluetoothType type, final String name, final String identifier) { + if( null == parent ) { + return findInCache(name, identifier, type); + } + final boolean anyType = BluetoothType.NONE == type; + final boolean deviceType = BluetoothType.DEVICE == type; + final boolean serviceType = BluetoothType.GATT_SERVICE == type; + final boolean charType = BluetoothType.GATT_CHARACTERISTIC== type; + final boolean descType = BluetoothType.GATT_DESCRIPTOR == type; + + final BluetoothType parentType = parent.getBluetoothType(); + + if( BluetoothType.ADAPTER == parentType && + ( anyType || deviceType || serviceType || charType || descType ) + ) + { + return ((DBTAdapter) parent).findInCache(name, identifier, type); + } + if( BluetoothType.DEVICE == parentType && + ( anyType || serviceType || charType || descType ) + ) + { + return ((DBTDevice) parent).findInCache(identifier, type); + } + if( BluetoothType.GATT_SERVICE == parentType && + ( anyType || charType || descType ) + ) + { + final DBTGattService service = (DBTGattService) parent; + if( !service.checkServiceCache() ) { + return null; + } + return service.findInCache(identifier, type); + } + if( BluetoothType.GATT_CHARACTERISTIC == parentType && + ( anyType || descType ) + ) + { + final DBTGattCharacteristic characteristic = (DBTGattCharacteristic) parent; + if( !characteristic.checkServiceCache() ) { + return null; + } + return characteristic.findInCache(identifier, type); + } + return null; + } + } |