Direct-BT  2.3.1
Direct-BT - Direct Bluetooth Programming.
DBTGattChar.java
Go to the documentation of this file.
1 /**
2  * Author: Sven Gothel <sgothel@jausoft.com>
3  * Copyright (c) 2020 Gothel Software e.K.
4  * Copyright (c) 2020 ZAFENA AB
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 
26 package jau.direct_bt;
27 
28 import java.lang.ref.WeakReference;
29 import java.util.List;
30 
32 import org.direct_bt.BTGattChar;
33 import org.direct_bt.BTGattDesc;
35 import org.direct_bt.BTObject;
36 import org.direct_bt.BTType;
37 import org.direct_bt.BTUtils;
40 
41 public class DBTGattChar extends DBTObject implements BTGattChar
42 {
43  private static final boolean DEBUG = DBTManager.DEBUG;
44 
45  /** Characteristics's service weak back-reference */
46  final WeakReference<DBTGattService> wbr_service;
47 
48  /**
49  * Characteristic Handle of this instance.
50  * <p>
51  * Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle).
52  * </p>
53  */
54  private final short handle;
55 
56  private final GattCharPropertySet properties;
57 
58  /* Characteristics Value Type UUID */
59  private final String value_type_uuid;
60 
61  /**
62  * Characteristics Value Handle.
63  * <p>
64  * Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle).
65  * </p>
66  */
67  private final short value_handle;
68 
69  /* Optional Client Characteristic Configuration index within descriptorList */
70  private final int clientCharacteristicsConfigIndex;
71 
72  /* pp */ final List<BTGattDesc> descriptorList;
73 
74  boolean enabledNotifyState = false;
75  boolean enabledIndicateState = false;
76 
77  /* pp */ DBTGattChar(final long nativeInstance, final DBTGattService service,
78  final short handle, final GattCharPropertySet properties,
79  final String value_type_uuid, final short value_handle,
80  final int clientCharacteristicsConfigIndex)
81  {
82  super(nativeInstance, handle /* hash */);
83  this.wbr_service = new WeakReference<DBTGattService>(service);
84  this.handle = handle;
85 
86  this.properties = properties;
87  this.value_type_uuid = value_type_uuid;
88  this.value_handle = value_handle;
89  this.clientCharacteristicsConfigIndex = clientCharacteristicsConfigIndex;
90  this.descriptorList = getDescriptorsImpl();
91 
92  if( DEBUG ) {
93  final boolean hasNotify = properties.isSet(GattCharPropertySet.Type.Notify);
94  final boolean hasIndicate = properties.isSet(GattCharPropertySet.Type.Indicate);
95 
96  if( hasNotify || hasIndicate ) {
97  final Listener characteristicListener = new Listener() {
98  @Override
99  public void notificationReceived(final BTGattChar charDecl, final byte[] value, final long timestamp) {
100  System.err.println("GATTCharacteristicListener.notificationReceived: "+charDecl+
101  ", value[len "+value.length+": "+BTUtils.bytesHexString(value, 0, -1, true)+"]");
102  }
103  @Override
104  public void indicationReceived(final BTGattChar charDecl, final byte[] value, final long timestamp,
105  final boolean confirmationSent) {
106  System.err.println("GATTCharacteristicListener.indicationReceived: "+charDecl+
107  ", value[len "+value.length+": "+BTUtils.bytesHexString(value, 0, -1, true)+
108  "], confirmationSent "+confirmationSent);
109  }
110  };
111  this.addCharListener(characteristicListener); // silent, don't enable native GATT ourselves
112  }
113  }
114  }
115 
116  @Override
117  public synchronized void close() {
118  if( !isValid() ) {
119  return;
120  }
122  super.close();
123  }
124 
125  @Override
126  public boolean equals(final Object obj)
127  {
128  if (obj == null || !(obj instanceof DBTGattChar)) {
129  return false;
130  }
131  final DBTGattChar other = (DBTGattChar)obj;
132  return handle == other.handle; /** unique attribute handles */
133  }
134 
135  @Override
136  public String getUUID() { return value_type_uuid; }
137 
138  @Override
139  public BTType getBluetoothType() { return class_type(); }
140 
141  static BTType class_type() { return BTType.GATT_CHARACTERISTIC; }
142 
143  @Override
144  public BTGattChar clone()
145  { throw new UnsupportedOperationException(); } // FIXME
146 
147  @Override
148  public BTGattDesc find(final String UUID, final long timeoutMS) {
149  if( !checkServiceCache() ) {
150  return null;
151  }
152  return (DBTGattDesc) findInCache(UUID, BTType.GATT_DESCRIPTOR);
153  }
154 
155  @Override
156  public BTGattDesc find(final String UUID) {
157  return find(UUID, 0);
158  }
159 
160  @Override
161  public final BTGattService getService() { return wbr_service.get(); }
162 
163  @Override
164  public final boolean getNotifying(final boolean enabledState[/*2*/]) {
165  enabledState[0] = enabledNotifyState;
166  enabledState[1] = enabledIndicateState;
167  return enabledNotifyState || enabledIndicateState;
168  }
169 
170  @Override
171  public final GattCharPropertySet getProperties() { return properties; }
172 
173  @Override
174  public final byte[] readValue() throws BTException {
175  return readValueImpl();
176  }
177 
178  @Override
179  public final boolean writeValue(final byte[] value, final boolean withResponse) throws BTException {
180  return writeValueImpl(value, withResponse);
181  }
182 
183  @Override
184  public final List<BTGattDesc> getDescriptors() { return descriptorList; }
185 
186  @Override
187  public final synchronized boolean configNotificationIndication(final boolean enableNotification, final boolean enableIndication, final boolean enabledState[/*2*/])
188  throws IllegalStateException
189  {
190  final boolean hasNotify = properties.isSet(GattCharPropertySet.Type.Notify);
191  final boolean hasIndicate = properties.isSet(GattCharPropertySet.Type.Indicate);
192 
193  if( hasNotify || hasIndicate ) {
194  final boolean resEnableNotification = hasNotify && enableNotification;
195  final boolean resEnableIndication = hasIndicate && enableIndication;
196 
197  if( resEnableNotification == enabledNotifyState &&
198  resEnableIndication == enabledIndicateState )
199  {
200  enabledState[0] = resEnableNotification;
201  enabledState[1] = resEnableIndication;
202  if( DEBUG ) {
203  System.err.printf("GATTCharacteristic.configNotificationIndication: Unchanged: notification[shall %b, has %b: %b == %b], indication[shall %b, has %b: %b == %b]\n",
204  enableNotification, hasNotify, enabledNotifyState, resEnableNotification,
205  enableIndication, hasIndicate, enabledIndicateState, resEnableIndication);
206  }
207  return true;
208  }
209 
210  final boolean res = configNotificationIndicationImpl(enableNotification, enableIndication, enabledState);
211  if( !res ) {
212  enabledState[0] = false;
213  enabledState[1] = false;
214  }
215  if( DEBUG ) {
216  System.err.printf("GATTCharacteristic.configNotificationIndication: res %b, notification[shall %b, has %b: %b -> %b], indication[shall %b, has %b: %b -> %b]\n",
217  res,
218  enableNotification, hasNotify, enabledNotifyState, enabledState[0],
219  enableIndication, hasIndicate, enabledIndicateState, enabledState[1]);
220  }
221  enabledNotifyState = enabledState[0];
222  enabledIndicateState = enabledState[1];
223  return res;
224  } else {
225  enabledState[0] = false;
226  enabledState[1] = false;
227  if( DEBUG ) {
228  System.err.println("GATTCharacteristic.configNotificationIndication: FALSE*: hasNotify "+hasNotify+", hasIndicate "+hasIndicate);
229  }
230  return false;
231  }
232  }
233  private native boolean configNotificationIndicationImpl(boolean enableNotification, boolean enableIndication, final boolean enabledState[/*2*/])
234  throws IllegalStateException;
235 
236  @Override
237  public boolean enableNotificationOrIndication(final boolean enabledState[/*2*/])
238  throws IllegalStateException
239  {
240  final boolean enableNotification = properties.isSet(GattCharPropertySet.Type.Notify);
241  final boolean enableIndication = !enableNotification && properties.isSet(GattCharPropertySet.Type.Indicate);
242 
243  return configNotificationIndication(enableNotification, enableIndication, enabledState);
244  }
245 
246  static private class DelegatedBTGattCharListener extends BTGattCharListener {
247  private final Listener delegate;
248 
249  public DelegatedBTGattCharListener(final BTGattChar characteristicMatch, final Listener l) {
250  super(characteristicMatch);
251  delegate = l;
252  }
253 
254  @Override
255  public void notificationReceived(final BTGattChar charDecl,
256  final byte[] value, final long timestamp) {
257  delegate.notificationReceived(charDecl, value, timestamp);
258  }
259 
260  @Override
261  public void indicationReceived(final BTGattChar charDecl,
262  final byte[] value, final long timestamp,
263  final boolean confirmationSent) {
264  delegate.indicationReceived(charDecl, value, timestamp, confirmationSent);
265  }
266  };
267 
268  @Override
269  public final boolean addCharListener(final Listener listener) {
270  return getService().getDevice().addCharListener( new DelegatedBTGattCharListener(this, listener) );
271  }
272 
273  @Override
274  public final boolean addCharListener(final Listener listener, final boolean enabledState[/*2*/]) {
275  if( !enableNotificationOrIndication(enabledState) ) {
276  return false;
277  }
278  return addCharListener( listener );
279  }
280 
281  @Override
282  public final int removeAllAssociatedCharListener(final boolean disableIndicationNotification) {
283  if( disableIndicationNotification ) {
284  configNotificationIndication(false /* enableNotification */, false /* enableIndication */, new boolean[2]);
285  }
287  }
288 
289  @Override
290  public final synchronized void disableValueNotifications() {
291  configNotificationIndication(false /* enableNotification */, false /* enableIndication */, new boolean[2]);
292  }
293 
294  /**
295  * Characteristic Handle of this instance.
296  * <p>
297  * Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle).
298  * </p>
299  */
300  public final short getHandle() { return handle; }
301 
302  /**
303  * Returns Characteristics Value Handle.
304  * <p>
305  * Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle).
306  * </p>
307  */
308  public final short getValueHandle() { return value_handle; }
309 
310  /** Returns optional Client Characteristic Configuration index within descriptorList */
311  public final int getClientCharacteristicsConfigIndex() { return clientCharacteristicsConfigIndex; }
312 
313  @Override
314  public final String toString() {
315  if( !isValid() ) {
316  return "Characteristic" + "\u271D" + "[uuid "+getUUID()+", handle 0x"+Integer.toHexString(handle)+"]";
317  }
318  return toStringImpl();
319  }
320 
321  /* Native method calls: */
322 
323  private native String toStringImpl();
324 
325  private native byte[] readValueImpl() throws BTException;
326 
327  private native boolean writeValueImpl(byte[] argValue, boolean withResponse) throws BTException;
328 
329  private native List<BTGattDesc> getDescriptorsImpl();
330 
331  @Override
332  protected native void deleteImpl(long nativeInstance);
333 
334  /* local functionality */
335 
336  /* pp */ boolean checkServiceCache() {
337  final DBTGattService service = wbr_service.get();
338  if( null == service ) {
339  return false;
340  }
341  final DBTDevice device = service.wbr_device.get();
342  return null != device && device.checkServiceCache(false);
343  }
344 
345  /**
346  * Returns the matching {@link DBTObject} from the internal cache if found,
347  * otherwise {@code null}.
348  * <p>
349  * The returned {@link DBTObject} may be of type
350  * <ul>
351  * <li>{@link DBTGattDesc}</li>
352  * </ul>
353  * or alternatively in {@link BTObject} space
354  * <ul>
355  * <li>{@link BTType#GATT_DESCRIPTOR} -> {@link BTGattDesc}</li>
356  * </ul>
357  * </p>
358  * @param uuid UUID of the desired
359  * {@link BTType#GATT_DESCRIPTOR descriptor} to be found.
360  * Maybe {@code null}, in which case the first object of the desired type is being returned - if existing.
361  * @param type specify the type of the object to be found, a {@link BTType#GATT_DESCRIPTOR descriptor}.
362  * {@link BTType#NONE none} means anything.
363  */
364  /* pp */ DBTObject findInCache(final String uuid, final BTType type) {
365  final boolean anyType = BTType.NONE == type;
366  final boolean descType = BTType.GATT_DESCRIPTOR == type;
367 
368  if( !anyType && !descType ) {
369  return null;
370  }
371  final int size = descriptorList.size();
372  for(int i = 0; i < size; i++ ) {
373  final DBTGattDesc descr = (DBTGattDesc) descriptorList.get(i);
374  if( null == uuid || descr.getUUID().equals(uuid) ) {
375  return descr;
376  }
377  }
378  return null;
379  }
380 }
jau.direct_bt.DBTGattChar.addCharListener
final boolean addCharListener(final Listener listener)
Add the given BTGattChar.Listener to the listener list if not already present.
Definition: DBTGattChar.java:269
jau.direct_bt.DBTGattChar.find
BTGattDesc find(final String UUID, final long timeoutMS)
Find a BluetoothGattDescriptor.
Definition: DBTGattChar.java:148
jau.direct_bt.DBTGattChar.enableNotificationOrIndication
boolean enableNotificationOrIndication(final boolean enabledState[])
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration.
Definition: DBTGattChar.java:237
org.direct_bt.BTGattService.getDevice
BTDevice getDevice()
Returns the device to which this service belongs to.
jau.direct_bt.DBTGattChar.getClientCharacteristicsConfigIndex
final int getClientCharacteristicsConfigIndex()
Returns optional Client Characteristic Configuration index within descriptorList.
Definition: DBTGattChar.java:311
jau.direct_bt.DBTGattService
Definition: DBTGattService.java:39
jau.direct_bt.DBTGattChar.configNotificationIndication
final synchronized boolean configNotificationIndication(final boolean enableNotification, final boolean enableIndication, final boolean enabledState[])
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration.
Definition: DBTGattChar.java:187
org.direct_bt
Author: Sven Gothel sgothel@jausoft.com Copyright (c) 2020 Gothel Software e.K.
org.direct_bt.BTType.GATT_DESCRIPTOR
GATT_DESCRIPTOR
Definition: BTType.java:31
org.direct_bt.BTType.GATT_CHARACTERISTIC
GATT_CHARACTERISTIC
Definition: BTType.java:30
jau.direct_bt.DBTGattChar.getService
final BTGattService getService()
Returns the service to which this characteristic belongs to.
Definition: DBTGattChar.java:161
org.direct_bt.GattCharPropertySet.isSet
boolean isSet(final Type bit)
Definition: GattCharPropertySet.java:63
org.direct_bt.GattCharPropertySet.Type.Notify
Notify
Definition: GattCharPropertySet.java:48
jau.direct_bt.DBTGattChar.readValue
final byte[] readValue()
Reads the value of this characteristic.
Definition: DBTGattChar.java:174
jau.direct_bt.DBTManager
Definition: DBTManager.java:51
jau.direct_bt.DBTGattChar.getDescriptors
final List< BTGattDesc > getDescriptors()
Returns a list of BluetoothGattDescriptors this characteristic exposes.
Definition: DBTGattChar.java:184
jau.direct_bt.DBTGattChar.removeAllAssociatedCharListener
final int removeAllAssociatedCharListener(final boolean disableIndicationNotification)
Disables the notification and/or indication for this characteristic BLE level if.
Definition: DBTGattChar.java:282
jau.direct_bt.DBTGattChar
Definition: DBTGattChar.java:42
org.direct_bt.BTGattService
Provides access to Bluetooth GATT characteristic.
Definition: BTGattService.java:41
jau.direct_bt.DBTDevice
Definition: DBTDevice.java:58
org.direct_bt.GattCharPropertySet.Type.Indicate
Indicate
Definition: GattCharPropertySet.java:49
org.direct_bt.BTGattChar.Listener
BTGattChar event listener for notification and indication events.
Definition: BTGattChar.java:56
jau.direct_bt.DBTGattChar.writeValue
final boolean writeValue(final byte[] value, final boolean withResponse)
Writes the value of this characteristic, using one of the following methods depending on.
Definition: DBTGattChar.java:179
jau.direct_bt.DBTGattChar.find
BTGattDesc find(final String UUID)
Find a BluetoothGattDescriptor.
Definition: DBTGattChar.java:156
org.direct_bt.BTDevice.addCharListener
boolean addCharListener(final BTGattCharListener listener)
Add the given BTGattCharListener to the listener list if not already present.
jau.direct_bt.DBTGattChar.disableValueNotifications
final synchronized void disableValueNotifications()
Disables notifications of the value and unregisters the callback object passed through the correspond...
Definition: DBTGattChar.java:290
jau.direct_bt.DBTGattChar.getNotifying
final boolean getNotifying(final boolean enabledState[])
Returns true if notification for changes of this characteristic are activated.
Definition: DBTGattChar.java:164
org.direct_bt.BTUtils
Definition: BTUtils.java:29
org.direct_bt.GattCharPropertySet.Type
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1.1 Characteristic Properties.
Definition: GattCharPropertySet.java:42
jau.direct_bt.DBTGattChar.getProperties
final GattCharPropertySet getProperties()
Returns the properties of this characteristic.
Definition: DBTGattChar.java:171
org.direct_bt.BTDevice.removeAllAssociatedCharListener
int removeAllAssociatedCharListener(final BTGattChar associatedCharacteristic)
Remove all BTGattCharListener from the list, which are associated to the given BTGattChar.
jau.direct_bt.DBTGattChar.getBluetoothType
BTType getBluetoothType()
Returns the BluetoothType of this object.
Definition: DBTGattChar.java:139
org.direct_bt.BTGattChar
Provides access to Bluetooth GATT characteristic.
Definition: BTGattChar.java:40
org.direct_bt.BTType.NONE
NONE
Definition: BTType.java:29
org.direct_bt.BTGattDesc
Provides access to Bluetooth GATT descriptor.
Definition: BTGattDesc.java:38
jau.direct_bt.DBTManager.DEBUG
static final boolean DEBUG
Definition: DBTManager.java:52
org
org.direct_bt.GattCharPropertySet
Bit mask of GATT Characteristic Properties.
Definition: GattCharPropertySet.java:35
org.direct_bt.BTType
Definition: BTType.java:28
org.direct_bt.BTException
Definition: BTException.java:32
jau.direct_bt.DBTGattChar.getValueHandle
final short getValueHandle()
Returns Characteristics Value Handle.
Definition: DBTGattChar.java:308
jau.direct_bt.DBTGattChar.clone
BTGattChar clone()
Definition: DBTGattChar.java:144
jau.direct_bt.DBTGattChar.getUUID
String getUUID()
Get the UUID of this characteristic.
Definition: DBTGattChar.java:136
jau.direct_bt.DBTGattChar.addCharListener
final boolean addCharListener(final Listener listener, final boolean enabledState[])
Add the given BTGattChar.Listener to the listener list if not already present and if enabling the not...
Definition: DBTGattChar.java:274
jau.direct_bt.DBTGattChar.deleteImpl
native void deleteImpl(long nativeInstance)
Deletes the native instance.
jau.direct_bt.DBTGattChar.close
synchronized void close()
Release the native memory associated with this object The object should not be used following a call ...
Definition: DBTGattChar.java:117
jau.direct_bt.DBTGattChar.equals
boolean equals(final Object obj)
Definition: DBTGattChar.java:126
org.direct_bt.BTGattCharListener
BTGattChar event listener for notification and indication events.
Definition: BTGattCharListener.java:57
org.direct_bt.BTObject
Definition: BTObject.java:31
jau.direct_bt.DBTGattChar.getHandle
final short getHandle()
Characteristic Handle of this instance.
Definition: DBTGattChar.java:300
org.direct_bt.BTUtils.bytesHexString
static String bytesHexString(final byte[] bytes, final int offset, final int length, final boolean lsbFirst)
Produce a lower-case hexadecimal string representation of the given byte values.
Definition: BTUtils.java:123
jau.direct_bt.DBTGattDesc
Definition: DBTGattDesc.java:36
jau.direct_bt.DBTGattChar.toString
final String toString()
Definition: DBTGattChar.java:314
jau.direct_bt.DBTObject
Definition: DBTObject.java:32