|
package com.clj.fastble.conn;
|
|
import android.bluetooth.BluetoothGatt;
|
import android.bluetooth.BluetoothGattCallback;
|
import android.bluetooth.BluetoothGattCharacteristic;
|
import android.bluetooth.BluetoothGattDescriptor;
|
import android.bluetooth.BluetoothGattService;
|
import android.os.Handler;
|
import android.os.Looper;
|
import android.os.Message;
|
|
import com.clj.fastble.bluetooth.BleBluetooth;
|
import com.clj.fastble.exception.GattException;
|
import com.clj.fastble.exception.OtherException;
|
import com.clj.fastble.exception.TimeoutException;
|
import com.clj.fastble.utils.BleLog;
|
import com.clj.fastble.utils.HexUtil;
|
|
import java.util.Arrays;
|
import java.util.UUID;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
/**
|
* Ble Device Connector.
|
* be sure main thread
|
*/
|
public class BleConnector {
|
|
private static final String TAG = BleConnector.class.getSimpleName();
|
private static final String UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR = "00002902-0000-1000-8000-00805f9b34fb";
|
|
private static final int MSG_WRITE_CHA = 1;
|
private static final int MSG_WRIATE_DES = 2;
|
private static final int MSG_READ_CHA = 3;
|
private static final int MSG_READ_DES = 4;
|
private static final int MSG_READ_RSSI = 5;
|
private static final int MSG_NOTIFY_CHA = 6;
|
private static final int MSG_NOTIY_DES = 7;
|
private static final int MSG_INDICATE_DES = 8;
|
|
private BluetoothGatt bluetoothGatt;
|
private BluetoothGattService service;
|
private BluetoothGattCharacteristic characteristic;
|
private BluetoothGattDescriptor descriptor;
|
private BleBluetooth bleBluetooth;
|
private static int timeOutMillis = 10000;
|
private Handler handler = new MyHandler();
|
|
private static final class MyHandler extends Handler {
|
@Override
|
public void handleMessage(Message msg) {
|
|
BleCallback call = (BleCallback) msg.obj;
|
if (call != null) {
|
call.onFailure(new TimeoutException());
|
}
|
msg.obj = null;
|
}
|
}
|
|
public BleConnector(BleBluetooth bleBluetooth) {
|
this.bleBluetooth = bleBluetooth;
|
this.bluetoothGatt = bleBluetooth.getBluetoothGatt();
|
this.handler = new Handler(Looper.getMainLooper());
|
}
|
|
public BleConnector(BleBluetooth bleBluetooth, BluetoothGattService service,
|
BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor) {
|
this(bleBluetooth);
|
this.service = service;
|
this.characteristic = characteristic;
|
this.descriptor = descriptor;
|
}
|
|
public BleConnector(BleBluetooth bleBluetooth,
|
UUID serviceUUID, UUID charactUUID,
|
UUID descriptorUUID, UUID client_characteristic_conifgUUID) {
|
this(bleBluetooth);
|
withUUID(serviceUUID, charactUUID, descriptorUUID);
|
}
|
|
public BleConnector(BleBluetooth bleBluetooth,
|
String serviceUUID, String charactUUID,
|
String descriptorUUID, String client_characteristic_conifgUUID) {
|
this(bleBluetooth);
|
withUUIDString(serviceUUID, charactUUID, descriptorUUID);
|
}
|
|
|
public BleConnector withUUID(UUID serviceUUID, UUID charactUUID, UUID descriptorUUID) {
|
|
if (serviceUUID != null && bluetoothGatt != null) {
|
service = bluetoothGatt.getService(serviceUUID);
|
}
|
|
if (service != null && charactUUID != null) {
|
characteristic = service.getCharacteristic(charactUUID);
|
}
|
|
if (characteristic != null && descriptorUUID != null) {
|
descriptor = characteristic.getDescriptor(descriptorUUID);
|
}
|
|
return this;
|
}
|
|
public BleConnector withUUIDString(String serviceUUID, String charactUUID,
|
String descriptorUUID) {
|
return withUUID(formUUID(serviceUUID), formUUID(charactUUID), formUUID(descriptorUUID));
|
}
|
|
private UUID formUUID(String uuid) {
|
return uuid == null ? null : UUID.fromString(uuid);
|
}
|
|
|
|
|
/*------------------------------- main operation ----------------------------------- */
|
|
|
/**
|
* notify
|
*/
|
public boolean enableCharacteristicNotify(BleCharacterCallback bleCallback, String uuid_notify) {
|
|
if (getCharacteristic() != null
|
&& (getCharacteristic().getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
|
BleLog.w(TAG, "characteristic.getProperties():" + getCharacteristic().getProperties());
|
|
handleCharacteristicNotificationCallback(bleCallback, uuid_notify);
|
|
return setCharacteristicNotification(getBluetoothGatt(), getCharacteristic(), true, bleCallback);
|
|
} else {
|
if (bleCallback != null) {
|
bleCallback.onFailure(new OtherException("this characteristic not support notify!"));
|
bleCallback.onInitiatedResult(false);
|
}
|
return false;
|
}
|
}
|
|
/**
|
* stop notify
|
*/
|
public boolean disableCharacteristicNotify() {
|
if (getCharacteristic() != null
|
&& (getCharacteristic().getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
|
BleLog.w(TAG, "characteristic.getProperties():" + getCharacteristic().getProperties());
|
|
return setCharacteristicNotification(getBluetoothGatt(), getCharacteristic(), false, null);
|
} else {
|
return false;
|
}
|
}
|
|
/**
|
* notify setting
|
*/
|
private boolean setCharacteristicNotification(BluetoothGatt gatt,
|
BluetoothGattCharacteristic characteristic,
|
boolean enable,
|
BleCharacterCallback bleCallback) {
|
if (gatt == null || characteristic == null) {
|
if (bleCallback != null) {
|
bleCallback.onFailure(new OtherException("gatt or characteristic equal null"));
|
bleCallback.onInitiatedResult(false);
|
}
|
return false;
|
}
|
|
boolean success = gatt.setCharacteristicNotification(characteristic, enable);
|
BleLog.d(TAG, "setCharacteristicNotification: " + enable
|
+ "\nsuccess: " + success
|
+ "\ncharacteristic.getUuid(): " + characteristic.getUuid());
|
|
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(formUUID(UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR));
|
if (descriptor != null) {
|
descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE :
|
BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
|
boolean success2 = gatt.writeDescriptor(descriptor);
|
if (bleCallback != null) {
|
bleCallback.onInitiatedResult(success2);
|
}
|
return success2;
|
}
|
if (bleCallback != null) {
|
bleCallback.onFailure(new OtherException("notify operation failed"));
|
bleCallback.onInitiatedResult(false);
|
}
|
return false;
|
}
|
|
/**
|
* indicate
|
*/
|
public boolean enableCharacteristicIndicate(BleCharacterCallback bleCallback, String uuid_indicate) {
|
if (getCharacteristic() != null
|
&& (getCharacteristic().getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
|
BleLog.w(TAG, "characteristic.getProperties():" + getCharacteristic().getProperties());
|
|
handleCharacteristicIndicationCallback(bleCallback, uuid_indicate);
|
|
return setCharacteristicIndication(getBluetoothGatt(), getCharacteristic(), true, bleCallback);
|
|
} else {
|
if (bleCallback != null) {
|
bleCallback.onFailure(new OtherException("this characteristic not support indicate!"));
|
}
|
return false;
|
}
|
}
|
|
|
/**
|
* stop indicate
|
*/
|
public boolean disableCharacteristicIndicate() {
|
if (getCharacteristic() != null
|
&& (getCharacteristic().getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
|
BleLog.w(TAG, "characteristic.getProperties():" + getCharacteristic().getProperties());
|
|
return setCharacteristicIndication(getBluetoothGatt(), getCharacteristic(), false, null);
|
|
} else {
|
return false;
|
}
|
}
|
|
/**
|
* indicate setting
|
*/
|
private boolean setCharacteristicIndication(BluetoothGatt gatt,
|
BluetoothGattCharacteristic characteristic,
|
boolean enable,
|
BleCharacterCallback bleCallback) {
|
if (gatt == null || characteristic == null) {
|
if (bleCallback != null) {
|
bleCallback.onFailure(new OtherException("gatt or characteristic equal null"));
|
bleCallback.onInitiatedResult(false);
|
}
|
return false;
|
}
|
|
boolean success = gatt.setCharacteristicNotification(characteristic, enable);
|
BleLog.d(TAG, "setCharacteristicIndication:" + enable
|
+ "\nsuccess:" + success
|
+ "\ncharacteristic.getUuid():" + characteristic.getUuid());
|
|
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(formUUID(UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR));
|
if (descriptor != null) {
|
descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE :
|
BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
|
boolean success2 = gatt.writeDescriptor(descriptor);
|
if (bleCallback != null) {
|
bleCallback.onInitiatedResult(success2);
|
}
|
return success2;
|
}
|
if (bleCallback != null) {
|
bleCallback.onFailure(new OtherException("indicate operation failed"));
|
bleCallback.onInitiatedResult(false);
|
}
|
return false;
|
}
|
|
|
/**
|
* write
|
*/
|
public boolean writeCharacteristic(byte[] data, BleCharacterCallback bleCallback, String uuid_write) {
|
if (data == null) {
|
if (bleCallback != null) {
|
bleCallback.onFailure(new OtherException("the data to be written is empty"));
|
bleCallback.onInitiatedResult(false);
|
}
|
return false;
|
}
|
|
if (getCharacteristic() == null
|
|| (getCharacteristic().getProperties() & (BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) == 0) {
|
if (bleCallback != null) {
|
bleCallback.onFailure(new OtherException("this characteristic not support write!"));
|
bleCallback.onInitiatedResult(false);
|
}
|
return false;
|
}
|
|
BleLog.d(TAG, getCharacteristic().getUuid()
|
+ "\ncharacteristic.getProperties():" + getCharacteristic().getProperties()
|
+ "\ncharacteristic.getValue(): " + Arrays.toString(getCharacteristic().getValue())
|
+ "\ncharacteristic write bytes: " + Arrays.toString(data)
|
+ "\nhex: " + HexUtil.encodeHexStr(data));
|
|
handleCharacteristicWriteCallback(bleCallback, uuid_write);
|
getCharacteristic().setValue(data);
|
return handleAfterInitialed(getBluetoothGatt().writeCharacteristic(getCharacteristic()), bleCallback);
|
}
|
|
/**
|
* read
|
*/
|
public boolean readCharacteristic(BleCharacterCallback bleCallback, String uuid_read) {
|
if (getCharacteristic() != null
|
&& (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
|
|
BleLog.d(TAG, getCharacteristic().getUuid()
|
+ "\ncharacteristic.getProperties(): " + getCharacteristic().getProperties()
|
+ "\ncharacteristic.getValue(): " + Arrays.toString(getCharacteristic().getValue()));
|
|
setCharacteristicNotification(getBluetoothGatt(), getCharacteristic(), false, bleCallback);
|
handleCharacteristicReadCallback(bleCallback, uuid_read);
|
return handleAfterInitialed(getBluetoothGatt().readCharacteristic(getCharacteristic()), bleCallback);
|
|
} else {
|
if (bleCallback != null) {
|
bleCallback.onFailure(new OtherException("this characteristic not support read!"));
|
bleCallback.onInitiatedResult(false);
|
}
|
return false;
|
}
|
}
|
|
/**
|
* rssi
|
*/
|
public boolean readRemoteRssi(BleRssiCallback bleCallback) {
|
handleRSSIReadCallback(bleCallback);
|
return handleAfterInitialed(getBluetoothGatt().readRemoteRssi(), bleCallback);
|
}
|
|
|
/**************************************** handle call back ******************************************/
|
|
/**
|
* notify
|
*/
|
private void handleCharacteristicNotificationCallback(final BleCharacterCallback bleCallback,
|
final String uuid_notify) {
|
if (bleCallback != null) {
|
|
listenAndTimer(bleCallback, MSG_NOTIFY_CHA, uuid_notify, new BluetoothGattCallback() {
|
AtomicBoolean msgRemoved = new AtomicBoolean(false);
|
|
@Override
|
public void onCharacteristicChanged(BluetoothGatt gatt,
|
BluetoothGattCharacteristic characteristic) {
|
|
if (!msgRemoved.getAndSet(true)) {
|
handler.removeMessages(MSG_NOTIFY_CHA, this);
|
}
|
|
if (characteristic.getUuid().equals(UUID.fromString(uuid_notify))) {
|
System.out.println("chenqi Hex ->" + dumpHex(characteristic.getValue()));
|
bleCallback.onSuccess(characteristic);
|
}
|
}
|
});
|
}
|
}
|
|
public String dumpHex(byte[] src) {
|
String num = "0123456789ABCDEF";
|
StringBuilder sb = new StringBuilder();
|
// sb.append("[ ");
|
for (byte aSrc : src) {
|
int high = aSrc >> 4 & 0x0f;
|
int low = aSrc & 0x0f;
|
sb.append(num.charAt(high)).append(num.charAt(low)).append(" ");
|
}
|
// sb.append(" ]");
|
return sb.toString();
|
}
|
|
/**
|
* indicate
|
*/
|
private void handleCharacteristicIndicationCallback(final BleCharacterCallback bleCallback,
|
final String uuid_indicate) {
|
if (bleCallback != null) {
|
|
listenAndTimer(bleCallback, MSG_INDICATE_DES, uuid_indicate, new BluetoothGattCallback() {
|
AtomicBoolean msgRemoved = new AtomicBoolean(false);
|
|
@Override
|
public void onCharacteristicChanged(BluetoothGatt gatt,
|
BluetoothGattCharacteristic characteristic) {
|
|
if (!msgRemoved.getAndSet(true)) {
|
handler.removeMessages(MSG_INDICATE_DES, this);
|
}
|
if (characteristic.getUuid().equals(UUID.fromString(uuid_indicate))) {
|
bleCallback.onSuccess(characteristic);
|
}
|
}
|
});
|
}
|
}
|
|
/**
|
* write
|
*/
|
private void handleCharacteristicWriteCallback(final BleCharacterCallback bleCallback,
|
final String uuid_write) {
|
if (bleCallback != null) {
|
|
listenAndTimer(bleCallback, MSG_WRITE_CHA, uuid_write, new BluetoothGattCallback() {
|
@Override
|
public void onCharacteristicWrite(BluetoothGatt gatt,
|
BluetoothGattCharacteristic characteristic, int status) {
|
handler.removeMessages(MSG_WRITE_CHA, this);
|
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
if (characteristic.getUuid().equals(UUID.fromString(uuid_write))) {
|
bleCallback.onSuccess(characteristic);
|
}
|
} else {
|
bleCallback.onFailure(new GattException(status));
|
}
|
}
|
});
|
}
|
}
|
|
/**
|
* read
|
*/
|
private void handleCharacteristicReadCallback(final BleCharacterCallback bleCallback,
|
final String uuid_read) {
|
if (bleCallback != null) {
|
listenAndTimer(bleCallback, MSG_READ_CHA, uuid_read, new BluetoothGattCallback() {
|
AtomicBoolean msgRemoved = new AtomicBoolean(false);
|
|
@Override
|
public void onCharacteristicRead(BluetoothGatt gatt,
|
BluetoothGattCharacteristic characteristic, int status) {
|
if (!msgRemoved.getAndSet(true)) {
|
handler.removeMessages(MSG_READ_CHA, this);
|
}
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
if (characteristic.getUuid().equals(UUID.fromString(uuid_read))) {
|
bleCallback.onSuccess(characteristic);
|
}
|
} else {
|
bleCallback.onFailure(new GattException(status));
|
}
|
}
|
});
|
}
|
}
|
|
/**
|
* rssi
|
*/
|
private void handleRSSIReadCallback(final BleRssiCallback bleCallback) {
|
|
if (bleCallback != null) {
|
listenAndTimer(bleCallback, MSG_READ_RSSI, BleBluetooth.READ_RSSI_KEY, new BluetoothGattCallback() {
|
@Override
|
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
|
handler.removeMessages(MSG_READ_RSSI, this);
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
bleCallback.onSuccess(rssi);
|
} else {
|
bleCallback.onFailure(new GattException(status));
|
}
|
}
|
});
|
}
|
}
|
|
private boolean handleAfterInitialed(boolean initiated, BleCallback bleCallback) {
|
if (bleCallback != null) {
|
if (!initiated) {
|
bleCallback.onFailure(new OtherException("write or read operation failed"));
|
}
|
bleCallback.onInitiatedResult(initiated);
|
}
|
return initiated;
|
}
|
|
|
/**
|
* listen bleBluetooth gatt callback, and send a delayed message.
|
*/
|
private void listenAndTimer(BleCallback bleCallback, int what, String uuid, BluetoothGattCallback callback) {
|
bleCallback.setBluetoothGattCallback(callback);
|
bleBluetooth.addGattCallback(uuid, callback);
|
|
Message msg = handler.obtainMessage(what, bleCallback);
|
handler.sendMessageDelayed(msg, timeOutMillis);
|
}
|
|
|
|
/*------------------------------- getter and setter ----------------------------------- */
|
|
|
public BluetoothGatt getBluetoothGatt() {
|
return bluetoothGatt;
|
}
|
|
public BleConnector setBluetoothGatt(BluetoothGatt bluetoothGatt) {
|
this.bluetoothGatt = bluetoothGatt;
|
return this;
|
}
|
|
public BluetoothGattService getService() {
|
return service;
|
}
|
|
public BleConnector setService(BluetoothGattService service) {
|
this.service = service;
|
return this;
|
}
|
|
public BluetoothGattCharacteristic getCharacteristic() {
|
return characteristic;
|
}
|
|
public BleConnector setCharacteristic(BluetoothGattCharacteristic characteristic) {
|
this.characteristic = characteristic;
|
return this;
|
}
|
|
public BluetoothGattDescriptor getDescriptor() {
|
return descriptor;
|
}
|
|
public BleConnector setDescriptor(BluetoothGattDescriptor descriptor) {
|
this.descriptor = descriptor;
|
return this;
|
}
|
|
public int getTimeOutMillis() {
|
return timeOutMillis;
|
}
|
|
public BleConnector setTimeOutMillis(int timeOutMillis) {
|
this.timeOutMillis = timeOutMillis;
|
return this;
|
}
|
}
|