28import org.jau.io.PrintUtil;
29import org.jau.util.BasicTypes;
51 public void run(
final BTGattChar charDecl,
byte[] char_value,
long timestampMS);
57 private final String name;
59 private final String service_uuid;
61 private final String cmd_uuid;
63 private final String rsp_uuid;
65 private final Object mtxRspReceived =
new Object();
67 private volatile byte[] rsp_data;
70 private int rspMinSize;
72 private boolean setup_done;
77 public ResponseCharListener(
final BTGattCmd source_) {
82 private void store(
final byte[] value) {
83 if(
null == source.rsp_data ) {
84 source.rsp_data = value;
86 final int new_size = source.rsp_data.length + value.length;
87 final byte[] new_buf =
new byte[new_size];
88 System.arraycopy(source.rsp_data, 0, new_buf, 0, source.rsp_data.length);
89 System.arraycopy(value, 0, new_buf, source.rsp_data.length, value.length);
90 source.rsp_data = new_buf;
95 public void notificationReceived(
final BTGattChar charDecl,
96 final byte[] value,
final long timestamp) {
97 synchronized( source.mtxRspReceived ) {
99 PrintUtil.fprintf_td(System.err,
"BTGattCmd.notificationReceived: Resp %s, value[%s]\n",
100 charDecl.toString(), BasicTypes.bytesHexString(value, 0, value.length,
true ));
103 if(
null != source.dataCallback ) {
104 source.dataCallback.
run(charDecl, value, timestamp);
106 source.mtxRspReceived.notifyAll();
111 public void indicationReceived(
final BTGattChar charDecl,
112 final byte[] value,
final long timestamp,
113 final boolean confirmationSent) {
114 synchronized( source.mtxRspReceived ) {
116 PrintUtil.fprintf_td(System.err,
"BTGattCmd.indicationReceived: Resp %s, value[%s]\n",
117 charDecl.toString(), BasicTypes.bytesHexString(value, 0, value.length,
true ));
120 if(
null != source.dataCallback ) {
121 source.dataCallback.
run(charDecl, value, timestamp);
123 source.mtxRspReceived.notifyAll();
127 private final ResponseCharListener rspCharListener;
128 private boolean verbose;
130 private boolean isConnected() {
return dev.
getConnected(); }
132 private boolean isResolvedEq() {
133 return null != cmdCharRef;
136 private String rspCharStr() {
return null != rspCharRef ? rspCharRef.
toString() :
"n/a"; }
138 private HCIStatusCode setup() {
140 return isResolvedEq() ? HCIStatusCode.SUCCESS : HCIStatusCode.NOT_SUPPORTED;
143 cmdCharRef =
null != service_uuid ? dev.
findGattChar(service_uuid, cmd_uuid)
145 if(
null == cmdCharRef ) {
147 PrintUtil.fprintf_td(System.err,
"Command not found: service %s, char %s\n", service_uuid, cmd_uuid);
149 return HCIStatusCode.NOT_SUPPORTED;
155 PrintUtil.fprintf_td(System.err,
"Command has no write property: %s\n", cmdCharRef.
toString());
158 return HCIStatusCode.NOT_SUPPORTED;
161 if(
null != rsp_uuid ) {
162 rspCharRef =
null != service_uuid ? dev.
findGattChar(service_uuid, rsp_uuid)
164 if(
null == rspCharRef ) {
166 PrintUtil.fprintf_td(System.err,
"Response not found: service %s, char %s\n", service_uuid, rsp_uuid);
169 return HCIStatusCode.NOT_SUPPORTED;
172 final boolean cccdEnableResult[] = {
false,
false };
173 if( rspCharRef.
addCharListener( rspCharListener, cccdEnableResult ) ) {
174 return HCIStatusCode.SUCCESS;
177 PrintUtil.fprintf_td(System.err,
"CCCD Notify/Indicate not supported on response %s\n", rspCharRef.
toString());
181 return HCIStatusCode.NOT_SUPPORTED;
183 }
catch (
final Exception e ) {
184 PrintUtil.fprintf_td(System.err,
"Exception caught for %s: %s\n", e.toString(),
toString());
187 return HCIStatusCode.TIMEOUT;
190 return HCIStatusCode.SUCCESS;
212 final boolean wasResolved = isResolvedEq();
223 if( !isConnected() ) {
226 if(
null != rspCharRefCopy) {
235 }
catch (
final Exception e ) {
236 PrintUtil.fprintf_td(System.err,
"Exception caught for %s: %s\n", e.toString(),
toString());
254 final String service_uuid_,
final String cmd_uuid_,
final String rsp_uuid_ )
257 service_uuid = service_uuid_;
258 cmd_uuid = cmd_uuid_;
259 rsp_uuid = rsp_uuid_;
267 rspCharListener =
new ResponseCharListener(
this);
279 public BTGattCmd(
final BTDevice dev_,
final String name_,
final String service_uuid_,
final String cmd_uuid_)
282 service_uuid = service_uuid_;
283 cmd_uuid = cmd_uuid_;
292 rspCharListener =
null;
329 private String rspDataToString() {
330 return null == rsp_data ?
"null" : BasicTypes.bytesHexString(rsp_data, 0, rsp_data.length,
true );
343 return HCIStatusCode.SUCCESS == setup();
345 return isResolvedEq();
361 public synchronized HCIStatusCode send(
final boolean prefNoAck,
final byte[] cmd_data,
final int timeoutMS) {
362 return sendImpl(prefNoAck, cmd_data, timeoutMS,
true);
377 return sendImpl(prefNoAck, cmd_data, 0,
false);
380 private synchronized HCIStatusCode sendImpl(
final boolean prefNoAck,
final byte[] cmd_data,
final int timeoutMS,
final boolean allowResponse) {
383 if( !isConnected() ) {
386 synchronized( mtxRspReceived ) {
388 if( HCIStatusCode.SUCCESS != res ) {
394 PrintUtil.fprintf_td(System.err,
"BTGattCmd.sendBlocking: Start: Cmd %s, args[%s], Resp %s, result[%s]",
395 cmdCharRef.
toString(), cmd_data.toString(),
396 rspCharStr(), rspDataToString());
399 final boolean hasWriteNoAck = cmdCharRef.
getProperties().
isSet(GattCharPropertySet.Type.WriteNoAck);
400 final boolean hasWriteWithAck = cmdCharRef.
getProperties().
isSet(GattCharPropertySet.Type.WriteWithAck);
402 final boolean prefWriteNoAck = hasWriteNoAck && ( prefNoAck || !hasWriteWithAck );
404 if( prefWriteNoAck ) {
406 if( !cmdCharRef.
writeValue(cmd_data,
false ) ) {
407 PrintUtil.fprintf_td(System.err,
"Write (noAck) to command failed: Cmd %s, args[%s]\n",
408 cmdCharRef.
toString(), BasicTypes.bytesHexString(cmd_data, 0, cmd_data.length,
true ));
409 res = HCIStatusCode.
FAILED;
411 }
catch (
final Throwable t ) {
412 PrintUtil.fprintf_td(System.err,
"Exception caught @ Write (noAck) to command failed: Cmd %s, args[%s]: %s\n",
413 cmdCharRef.
toString(), BasicTypes.bytesHexString(cmd_data, 0, cmd_data.length,
true ), t.toString());
416 }
else if( hasWriteWithAck ) {
418 if( !cmdCharRef.
writeValue(cmd_data,
true ) ) {
419 PrintUtil.fprintf_td(System.err,
"Write (withAck) to command failed: Cmd %s, args[%s]\n",
420 cmdCharRef.
toString(), BasicTypes.bytesHexString(cmd_data, 0, cmd_data.length,
true ));
423 }
catch (
final Throwable t ) {
424 PrintUtil.fprintf_td(System.err,
"Exception caught @ Write (withAck) to command failed: Cmd %s, args[%s]: %s\n",
425 cmdCharRef.
toString(), BasicTypes.bytesHexString(cmd_data, 0, cmd_data.length,
true ), t.toString());
429 PrintUtil.fprintf_td(System.err,
"Command has no write property: %s\n", cmdCharRef.
toString());
430 res = HCIStatusCode.
FAILED;
433 if(
null != rspCharRef && allowResponse ) {
434 while( HCIStatusCode.SUCCESS == res &&
435 (
null == rsp_data || rspMinSize > rsp_data.length || ( 0 == rspMinSize && 0 == rsp_data.length ) )
438 if( 0 == timeoutMS ) {
440 mtxRspReceived.wait();
441 }
catch (
final InterruptedException e) { }
444 mtxRspReceived.wait(timeoutMS);
445 }
catch (
final Throwable t) {}
446 if(
null == rsp_data ) {
447 PrintUtil.fprintf_td(System.err,
"BTGattCmd.sendBlocking: Timeout: Cmd %s, args[%s]\n",
448 cmdCharRef.
toString(), BasicTypes.bytesHexString(cmd_data, 0, cmd_data.length,
true ));
455 if( DEBUG && HCIStatusCode.SUCCESS == res ) {
456 PrintUtil.fprintf_td(System.err,
"BTGattCmd.sendBlocking: OK: Cmd %s, args[%s], Resp %s, result[%s]\n",
457 cmdCharRef.
toString(), BasicTypes.bytesHexString(cmd_data, 0, cmd_data.length,
true ),
458 rspCharStr(), rspDataToString());
465 return "BTGattCmd["+dev.
getName()+
":"+name+
", service "+service_uuid+
466 ", char[cmd "+cmd_uuid+
", rsp "+rsp_uuid+
467 ", set["+setup_done+
", resolved "+isResolvedEq()+
"]]]";
One stop BTManager API entry point.
static final boolean DEBUG
Debug logging enabled or disabled.
BTGattChar event listener for notification and indication events.
Class maps a GATT command and optionally its asynchronous response to a synchronous atomic operation.
String getCommandUUID()
Return command's BTGattChar value jau::uuid_t to write command, never null.
String getServiceUUID()
Return command's BTGattService jau::uuid_t, may be null.
void setVerbose(final boolean v)
Set verbosity for UUID resolution.
synchronized HCIStatusCode send(final boolean prefNoAck, final byte[] cmd_data, final int timeoutMS)
Send the command to the remote BTDevice.
void close()
Close this command instance, usually called at destruction.
byte[] getResponse()
Returns the read-only response data object for configured commands with response notification or indi...
synchronized boolean isResolved()
Query whether all UUIDs of this commands have been resolved.
boolean hasResponseSet()
Return true if a notification or indication response has been set via constructor,...
String getName()
Return name, representing the command.
synchronized HCIStatusCode close0()
Close this command instance, usually called at destruction.
String getResponseUUID()
Return command's optional BTGattChar value jau::uuid_t for the notification or indication response,...
synchronized HCIStatusCode sendOnly(final boolean prefNoAck, final byte[] cmd_data)
Send the command to the remote BTDevice, only.
BTGattCmd(final BTDevice dev_, final String name_, final String service_uuid_, final String cmd_uuid_)
Constructor for commands without response.
BTGattCmd(final BTDevice dev_, final String name_, final String service_uuid_, final String cmd_uuid_, final String rsp_uuid_)
Constructor for commands with notification or indication response.
void setResponseMinSize(final int v)
void setDataCallback(final DataCallback dcb)
boolean isSet(final Type bit)
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
BTDevice represents one remote Bluetooth device.
String getName()
Returns the remote device name.
boolean getConnected()
Returns the connected state of the device.
BTGattChar findGattChar(String service_uuid, String char_uuid)
Find a BTGattChar by its service_uuid and char_uuid.
Representing a Gatt Characteristic object from the GATT client perspective.
boolean writeValue(byte[] argValue, boolean withResponse)
Writes the value of this characteristic, using one of the following methods depending on withRespons...
boolean removeCharListener(final BTGattCharListener listener)
Remove the given associated BTGattCharListener from the listener list if present.
boolean addCharListener(final BTGattCharListener listener)
Add the given BTGattCharListener to the listener list if not already present.
boolean disableIndicationNotification()
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration.
GattCharPropertySet getProperties()
Returns the properties of this characteristic.
void run(final BTGattChar charDecl, byte[] char_value, long timestampMS)