package nl.littlerobots.bean.internal.ble;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.util.Log;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.UUID;
import nl.littlerobots.bean.internal.device.DeviceProfile;
import nl.littlerobots.bean.internal.serial.GattSerialTransportProfile;
public class GattClient {
private static final byte[] sLock = new byte[0];
private static final String TAG = "GattClient";
private final GattSerialTransportProfile mSerialProfile;
private final DeviceProfile mDeviceProfile;
private BluetoothGatt mGatt;
private List<BaseProfile> mProfiles = new ArrayList<>(10);
private Queue<Runnable> mOperationsQueue = new ArrayDeque<>(32);
private boolean mOperationInProgress = false;
private boolean mConnected = false;
private boolean mDiscoveringServices = false;
private boolean mReconnect = false;
private final BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.d(TAG, "onConnectionStateChange " + newState);
if (status != BluetoothGatt.GATT_SUCCESS) {
disconnect();
return;
}
if (newState == BluetoothGatt.STATE_CONNECTED) {
mReconnect = true;
}
fireConnectionStateChange(newState);
if (newState == BluetoothGatt.STATE_DISCONNECTED && mReconnect) {
connect();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
mDiscoveringServices = false;
if (status != BluetoothGatt.GATT_SUCCESS) {
disconnect();
return;
}
fireServicesDiscovered();
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.d(TAG, "onCharacteristicRead");
if (status != BluetoothGatt.GATT_SUCCESS) {
disconnect();
return;
}
fireCharacteristicsRead(characteristic);
executeNextOperation();
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.d(TAG, "onCharacteristicWrite");
if (status != BluetoothGatt.GATT_SUCCESS) {
disconnect();
return;
}
fireCharacteristicWrite(characteristic);
executeNextOperation();
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
fireCharacteristicChanged(characteristic);
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
if (status != BluetoothGatt.GATT_SUCCESS) {
disconnect();
return;
}
fireDescriptorRead(descriptor);
executeNextOperation();
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
if (status != BluetoothGatt.GATT_SUCCESS) {
disconnect();
return;
}
fireDescriptorWrite(descriptor);
executeNextOperation();
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
if (status != BluetoothGatt.GATT_SUCCESS) {
disconnect();
return;
}
}
};
public GattClient() {
mSerialProfile = new GattSerialTransportProfile(this);
mDeviceProfile = new DeviceProfile(this);
mProfiles.add(mSerialProfile);
mProfiles.add(mDeviceProfile);
}
private void fireDescriptorRead(BluetoothGattDescriptor descriptor) {
for (BaseProfile profile : mProfiles) {
profile.onDescriptorRead(this, descriptor);
}
}
private synchronized void queueOperation(Runnable operation) {
mOperationsQueue.offer(operation);
if (!mOperationInProgress) {
executeNextOperation();
}
}
private synchronized void executeNextOperation() {
Runnable operation = mOperationsQueue.poll();
if (operation != null) {
mOperationInProgress = true;
operation.run();
} else {
mOperationInProgress = false;
}
}
public void connect(Context context, BluetoothDevice device) {
if (mGatt != null) {
mGatt.disconnect();
mGatt.close();
}
mConnected = false;
mReconnect = true;
mGatt = device.connectGatt(context, false, mBluetoothGattCallback);
}
private void fireDescriptorWrite(BluetoothGattDescriptor descriptor) {
for (BaseProfile profile : mProfiles) {
profile.onDescriptorWrite(this, descriptor);
}
}
private void fireCharacteristicChanged(BluetoothGattCharacteristic characteristic) {
for (BaseProfile profile : mProfiles) {
profile.onCharacteristicChanged(this, characteristic);
}
}
private void fireCharacteristicWrite(BluetoothGattCharacteristic characteristic) {
for (BaseProfile profile : mProfiles) {
profile.onCharacteristicWrite(this, characteristic);
}
}
private void fireCharacteristicsRead(BluetoothGattCharacteristic characteristic) {
for (BaseProfile profile : mProfiles) {
profile.onCharacteristicRead(this, characteristic);
}
}
private void fireServicesDiscovered() {
for (BaseProfile profile : mProfiles) {
profile.onServicesDiscovered(this);
}
}
private synchronized void fireConnectionStateChange(int newState) {
if (newState == BluetoothGatt.STATE_DISCONNECTED) {
mOperationsQueue.clear();
mOperationInProgress = false;
mConnected = false;
} else if (newState == BluetoothGatt.STATE_CONNECTED) {
mConnected = true;
}
for (BaseProfile profile : mProfiles) {
profile.onConnectionStateChange(newState);
}
}
public List<BluetoothGattService> getServices() {
return mGatt.getServices();
}
public BluetoothGattService getService(UUID uuid) {
return mGatt.getService(uuid);
}
public boolean discoverServices() {
if (mDiscoveringServices) {
return true;
}
mDiscoveringServices = true;
return mGatt.discoverServices();
}
public synchronized boolean readCharacteristic(final BluetoothGattCharacteristic characteristic) {
queueOperation(new Runnable() {
@Override
public void run() {
if (mGatt != null) {
mGatt.readCharacteristic(characteristic);
}
}
});
return true;
}
public synchronized boolean writeCharacteristic(final BluetoothGattCharacteristic characteristic) {
queueOperation(new Runnable() {
@Override
public void run() {
if (mGatt != null) {
mGatt.writeCharacteristic(characteristic);
}
}
});
return true;
}
public boolean readDescriptor(final BluetoothGattDescriptor descriptor) {
queueOperation(new Runnable() {
@Override
public void run() {
if (mGatt != null) {
mGatt.readDescriptor(descriptor);
}
}
});
return true;
}
public boolean writeDescriptor(final BluetoothGattDescriptor descriptor) {
queueOperation(new Runnable() {
@Override
public void run() {
if (mGatt != null) {
mGatt.writeDescriptor(descriptor);
}
}
});
return true;
}
public boolean readRemoteRssi() {
return mGatt.readRemoteRssi();
}
public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable) {
return mGatt.setCharacteristicNotification(characteristic, enable);
}
private boolean connect() {
Log.d(TAG, "connect");
return mGatt != null && mGatt.connect();
}
public void disconnect() {
mReconnect = false;
if (mGatt != null) {
mGatt.disconnect();
}
}
public void close() {
if (mGatt != null) {
mGatt.close();
}
mGatt = null;
}
public GattSerialTransportProfile getSerialProfile() {
return mSerialProfile;
}
public DeviceProfile getDeviceProfile() {
return mDeviceProfile;
}
}