Android Open Source - BetterBluetoothLE Async Bluetooth Gatt






From Project

Back to project page BetterBluetoothLE.

License

The source code is released under:

MIT License

If you think the Android project BetterBluetoothLE listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package betterbluetoothle.async;
//from ww  w.  j  a v  a 2 s. com
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 com.google.common.base.Objects;
import org.jdeferred.Promise;
import org.jdeferred.impl.DeferredObject;

import java.util.HashMap;
import java.util.List;
import java.util.UUID;

public class AsyncBluetoothGatt extends BluetoothGattCallback {

    private BluetoothDevice device;
    private Context context;
    private boolean autoConnect;
    private BluetoothGatt gatt;

    // References to all the deferred objects which will pass around the success/failure state of async actions.
    private DeferredObject<Void, Integer, Void> connect;
    private DeferredObject<Void, Integer, Void> disconnect;
    private DeferredObject<Void, Integer, Void> discoverServices;
    private DeferredObject<Void, Integer, Void> executeReliableWrite;
    private DeferredObject<Integer, Integer, Void> readRemoteRssi;
    private HashMap<CharacteristicKey, DeferredObject<BluetoothGattCharacteristic, Integer, Void>> readCharacteristic;
    private HashMap<CharacteristicKey, DeferredObject<BluetoothGattCharacteristic, Integer, Void>> writeCharacteristic;
    private HashMap<CharacteristicKey, DeferredObject<Void, Void, BluetoothGattCharacteristic>> changeCharacteristic;
    private HashMap<DescriptorKey, DeferredObject<BluetoothGattDescriptor, Integer, Void>> readDescriptor;
    private HashMap<DescriptorKey, DeferredObject<BluetoothGattDescriptor, Integer, Void>> writeDescriptor;

    // Key for uniquely identifying a characteristic is composed of:
    // - Characteristic UUID + instance ID
    // - Parent service UUID + instance ID
    private class CharacteristicKey {
        private UUID service;
        private int serviceInstance;
        private UUID characteristic;
        private int charInstance;
        public CharacteristicKey(BluetoothGattCharacteristic characteristic) {
            this.service = characteristic.getService().getUuid();
            this.serviceInstance = characteristic.getService().getInstanceId();
            this.characteristic = characteristic.getUuid();
            this.charInstance = characteristic.getInstanceId();
        }
        @Override
        public int hashCode() {
            return Objects.hashCode(service, serviceInstance, characteristic, charInstance);
        }
        @Override
        public boolean equals(Object o) {
            if (o == null) {
                return false;
            }
            if (o == this) {
                return true;
            }
            if (!(o instanceof CharacteristicKey)) {
                return false;
            }
            CharacteristicKey other = (CharacteristicKey)o;
            return Objects.equal(service, other.service) &&
                   Objects.equal(serviceInstance, other.serviceInstance) &&
                   Objects.equal(characteristic, other.characteristic) &&
                   Objects.equal(charInstance, other.charInstance);
        }
    }

    // Key for uniquely identifying a descriptor is composed of:
    // - Descriptor UUID
    // - Parent characteristic UUID + instance ID
    // - Parent service UUID + instance ID
    private class DescriptorKey {
        private UUID service;
        private int serviceInstance;
        private UUID characteristic;
        private int charInstance;
        private UUID descriptor;
        public DescriptorKey(BluetoothGattDescriptor descriptor) {
            this.service = descriptor.getCharacteristic().getService().getUuid();
            this.serviceInstance = descriptor.getCharacteristic().getService().getInstanceId();
            this.characteristic = descriptor.getCharacteristic().getUuid();
            this.charInstance = descriptor.getCharacteristic().getInstanceId();
            this.descriptor = descriptor.getUuid();
        }
        @Override
        public int hashCode() {
            return Objects.hashCode(service, serviceInstance, characteristic, charInstance, descriptor);
        }
        @Override
        public boolean equals(Object o) {
            if (o == null) {
                return false;
            }
            if (o == this) {
                return true;
            }
            if (!(o instanceof DescriptorKey)) {
                return false;
            }
            DescriptorKey other = (DescriptorKey)o;
            return Objects.equal(service, other.service) &&
                    Objects.equal(serviceInstance, other.serviceInstance) &&
                    Objects.equal(characteristic, other.characteristic) &&
                    Objects.equal(charInstance, other.charInstance) &&
                    Objects.equal(descriptor, other.descriptor);
        }
    }

    public AsyncBluetoothGatt(BluetoothDevice device, Context context, boolean autoConnect) {
        this.device = device;
        this.context = context;
        this.autoConnect = autoConnect;
        readCharacteristic = new HashMap<CharacteristicKey, DeferredObject<BluetoothGattCharacteristic, Integer, Void>>();
        writeCharacteristic = new HashMap<CharacteristicKey, DeferredObject<BluetoothGattCharacteristic, Integer, Void>>();
        readDescriptor = new HashMap<DescriptorKey, DeferredObject<BluetoothGattDescriptor, Integer, Void>>();
        writeDescriptor = new HashMap<DescriptorKey, DeferredObject<BluetoothGattDescriptor, Integer, Void>>();
        changeCharacteristic = new HashMap<CharacteristicKey, DeferredObject<Void, Void, BluetoothGattCharacteristic>>();
    }

    private void checkConnected() {
        if (gatt == null) {
            throw new RuntimeException("GATT is not connected.");
        }
    }

    private void resetDeferreds() {
        // TODO Stop in progress deferreds before resetting?
        connect = new DeferredObject<Void, Integer, Void>();
        disconnect = new DeferredObject<Void, Integer, Void>();
        discoverServices = null;
        executeReliableWrite = null;
        readRemoteRssi = null;
        readCharacteristic.clear();
        writeCharacteristic.clear();
        changeCharacteristic.clear();
        readDescriptor.clear();
        writeDescriptor.clear();
    }

    // Return GATT instance.
    public BluetoothGatt getGatt() {
        return gatt;
    }

    // Connect or reconnect to the device and return a promise for its completion.
    public Promise<Void, Integer, Void> connect() {
        // Handle connecting for the first time.
        if (connect == null) {
            resetDeferreds();
            gatt = device.connectGatt(context, autoConnect, this);
            if (gatt == null) {
                // Immediate error if the connect failed to return a gatt.
                connect.reject(null);
            }
        }
        // Reconnect if connect is called again.
        else if (!connect.isPending()) {
            resetDeferreds();
            if (!gatt.connect()) {
                connect.reject(null);
            }
        }
        return connect.promise();
    }

    // Return the disconnected promise.
    public Promise<Void, Integer, Void> disconnected() {
        return disconnect;
    }

    // Start service discovery and return a promise for its completion.
    public Promise<Void, Integer, Void> discoverServices() {
        checkConnected();
        // If there's already a request in flight, return the current promise for results.
        if (discoverServices != null && discoverServices.isPending()) {
            return discoverServices.promise();
        }
        // Start service discovery.
        discoverServices = new DeferredObject<Void, Integer, Void>();
        if (!gatt.discoverServices()) {
            // Immediate error if the service discovery failed to start.
            discoverServices.reject(null);
        }
        return discoverServices.promise();
    }

    // Request remote signal strength and return a promise for its completion.
    public Promise<Integer, Integer, Void> readRemoteRssi() {
        checkConnected();
        // If there's already a request in flight, return the current promise for results.
        if (readRemoteRssi != null && readRemoteRssi.isPending()) {
            return readRemoteRssi.promise();
        }
        // Request remote RSSI and return a promise for the results.
        readRemoteRssi = new DeferredObject<Integer, Integer, Void>();
        if (!gatt.readRemoteRssi()) {
            // Immediate error if the read RSSI call failed.
            readRemoteRssi.reject(null);
        }
        return readRemoteRssi.promise();
    }

    // Execute reliable write transaction and return a promise for its completion.
    public Promise<Void, Integer, Void> executeReliableWrite() {
        checkConnected();
        // If there's already a request in flight, return the current promise for results.
        if (executeReliableWrite != null && executeReliableWrite.isPending()) {
            return executeReliableWrite.promise();
        }
        // Execute reliable write and return promise for result.
        executeReliableWrite = new DeferredObject<Void, Integer, Void>();
        if (!gatt.executeReliableWrite()) {
            executeReliableWrite.reject(null);
        }
        return executeReliableWrite.promise();
    }

    // Start reliable write transaction.
    public boolean beginReliableWrite() {
        return gatt.beginReliableWrite();
    }

    // Abort in progress reliable write transaction.
    public void abortReliableWrite() {
        gatt.abortReliableWrite();
        // TODO Should you really reject any in progress deferred?
        if (executeReliableWrite != null && executeReliableWrite.isPending()) {
            executeReliableWrite.reject(null);
        }
    }

    // Disconnect from the device.
    public void disconnect() {
        checkConnected();
        gatt.disconnect();
        if (disconnect != null && disconnect.isPending()) {
            // Resolve disconnect deferred.
            disconnect.resolve(null);
        }
    }

    // Close the device.
    public void close() {
        disconnect();
        gatt.close();
        gatt = null;
    }

    // Get service with associated UUID.
    public BluetoothGattService getService(UUID uuid) {
        checkConnected();
        // Check that services have been discovered.
        if (discoverServices == null || discoverServices.isPending() || discoverServices.isRejected()) {
            throw new RuntimeException("Service discovery was not done or failed.");
        }
        return gatt.getService(uuid);
    }

    // Get list of services.
    public List<BluetoothGattService> getServices() {
        checkConnected();
        // Check that services have been discovered.
        if (discoverServices == null || discoverServices.isPending() || discoverServices.isRejected()) {
            throw new RuntimeException("Service discovery was not done or failed.");
        }
        return gatt.getServices();
    }

    // Get device instance.
    public BluetoothDevice getDevice() {
        return gatt.getDevice();
    }

    // Read characteristic and return promise for its completion.
    public Promise<BluetoothGattCharacteristic, Integer, Void> readCharacteristic(BluetoothGattCharacteristic characteristic) {
        checkConnected();
        // If there's already a request in flight, return the current promise for results.
        DeferredObject<BluetoothGattCharacteristic, Integer, Void> deferred = readCharacteristic.get(new CharacteristicKey(characteristic));
        if (deferred != null && deferred.isPending()) {
            return deferred.promise();
        }
        // Read descriptor and return a promise for the results.
        deferred = new DeferredObject<BluetoothGattCharacteristic, Integer, Void>();
        if (!gatt.readCharacteristic(characteristic)) {
            deferred.reject(null);
        }
        readCharacteristic.put(new CharacteristicKey(characteristic), deferred);
        return deferred.promise();
    }

    // Write characteristic and return promise for its completion.
    public Promise<BluetoothGattCharacteristic, Integer, Void> writeCharacteristic(BluetoothGattCharacteristic characteristic) {
        checkConnected();
        // If there's already a request in flight, return the current promise for results.
        DeferredObject<BluetoothGattCharacteristic, Integer, Void> deferred = writeCharacteristic.get(new CharacteristicKey(characteristic));
        if (deferred != null && deferred.isPending()) {
            return deferred.promise();
        }
        // Read descriptor and return a promise for the results.
        deferred = new DeferredObject<BluetoothGattCharacteristic, Integer, Void>();
        if (!gatt.writeCharacteristic(characteristic)) {
            deferred.reject(null);
        }
        writeCharacteristic.put(new CharacteristicKey(characteristic), deferred);
        return deferred.promise();
    }

    // Enable or disable notifications for characteristic changes.  The returned promise will notify
    // characteristic changes through its progress update.
    public Promise<Void, Void, BluetoothGattCharacteristic> setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable) {
        checkConnected();
        DeferredObject<Void, Void, BluetoothGattCharacteristic> deferred = changeCharacteristic.get(new CharacteristicKey(characteristic));
        // Handle when enable has already been called.
        if (enable && deferred != null && deferred.isPending()) {
            // Return the in progress deferred.
            return deferred.promise();
        }
        // Handle disable for an already established notification.
        else if (!enable && deferred != null) {
            // Finish the current deferred.
            deferred.resolve(null);
            changeCharacteristic.remove(new CharacteristicKey(characteristic));
            // Disable notifications.
            deferred = new DeferredObject<Void, Void, BluetoothGattCharacteristic>();
            if (!gatt.setCharacteristicNotification(characteristic, false)) {
                deferred.reject(null);
            }
            else {
                deferred.resolve(null);
            }
            // Return a deferred to give success/failure of the disable.
            // This deferred is immediately resolved or rejected because there is no async indication
            // of the setCharacteristicNotification finishing (only progress updates).
            return deferred.promise();
        }
        // Handle enabling notifications.
        else if (enable) {
            // Setup and return the deferred for receiving progress of notification changes.
            deferred = new DeferredObject<Void, Void, BluetoothGattCharacteristic>();
            if (!gatt.setCharacteristicNotification(characteristic, true)) {
                deferred.reject(null);
            }
            changeCharacteristic.put(new CharacteristicKey(characteristic), deferred);
            return deferred.promise();
        }
        // Ignore disabling a notification that isn't enabled.
        else {
            // Return a successfull result immediately.
            deferred = new DeferredObject<Void, Void, BluetoothGattCharacteristic>();
            deferred.resolve(null);
            return deferred.promise();
        }
    }

    // Read descriptor value and return a promise for its completion.
    public Promise<BluetoothGattDescriptor, Integer, Void> readDescriptor(BluetoothGattDescriptor descriptor) {
        checkConnected();
        // If there's already a request in flight, return the current promise for results.
        DeferredObject<BluetoothGattDescriptor, Integer, Void> deferred = readDescriptor.get(new DescriptorKey(descriptor));
        if (deferred != null && deferred.isPending()) {
            return deferred.promise();
        }
        // Read descriptor and return a promise for the results.
        deferred = new DeferredObject<BluetoothGattDescriptor, Integer, Void>();
        if (!gatt.readDescriptor(descriptor)) {
            deferred.reject(null);
        }
        readDescriptor.put(new DescriptorKey(descriptor), deferred);
        return deferred.promise();
    }

    // Write descriptor value and return a promise for its completion.
    public Promise<BluetoothGattDescriptor, Integer, Void> writeDescriptor(BluetoothGattDescriptor descriptor) {
        checkConnected();
        // If there's already a request in flight, return the current promise for results.
        DeferredObject<BluetoothGattDescriptor, Integer, Void> deferred = writeDescriptor.get(new DescriptorKey(descriptor));
        if (deferred != null && deferred.isPending()) {
            return deferred.promise();
        }
        // Write descriptor and return a promise for the results.
        deferred = new DeferredObject<BluetoothGattDescriptor, Integer, Void>();
        if (!gatt.writeDescriptor(descriptor)) {
            deferred.reject(null);
        }
        writeDescriptor.put(new DescriptorKey(descriptor), deferred);
        return deferred.promise();
    }

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        // TODO Check gatt passed in equals the expected gatt?
        if (newState == BluetoothGatt.STATE_CONNECTED && connect != null) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                connect.resolve(null);
            }
            else {
                connect.reject(status);
            }
        }
        else if (newState == BluetoothGatt.STATE_DISCONNECTED && disconnect != null) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                disconnect.resolve(null);
            }
            else {
                disconnect.reject(status);
            }
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        if (discoverServices != null) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                discoverServices.resolve(null);
            }
            else {
                discoverServices.reject(status);
            }
        }
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicRead(gatt, characteristic, status);
        DeferredObject<BluetoothGattCharacteristic, Integer, Void> deferred = readCharacteristic.get(new CharacteristicKey(characteristic));
        if (deferred != null) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                deferred.resolve(characteristic);
            }
            else {
                deferred.reject(status);
            }
        }
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);
        DeferredObject<Void, Void, BluetoothGattCharacteristic> deferred = changeCharacteristic.get(new CharacteristicKey(characteristic));
        if (deferred != null) {
            // Send a progress update with the changed characteristic.
            deferred.notify(characteristic);
        }
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicRead(gatt, characteristic, status);
        DeferredObject<BluetoothGattCharacteristic, Integer, Void> deferred = writeCharacteristic.get(new CharacteristicKey(characteristic));
        if (deferred != null) {
            // Resolve or reject the deferred based on success or failure of the characteristic write.
            if (status == BluetoothGatt.GATT_SUCCESS) {
                deferred.resolve(characteristic);
            }
            else {
                deferred.reject(status);
            }
        }
    }

    @Override
    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        super.onDescriptorRead(gatt, descriptor, status);
        DeferredObject<BluetoothGattDescriptor, Integer, Void> deferred = readDescriptor.get(new DescriptorKey(descriptor));
        if (deferred != null) {
            // Resolve or reject the deferred based on success or failure of the descriptor read.
            if (status == BluetoothGatt.GATT_SUCCESS) {
                deferred.resolve(descriptor);
            }
            else {
                deferred.reject(status);
            }
        }
    }

    @Override
    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
        super.onReliableWriteCompleted(gatt, status);
        if (executeReliableWrite != null) {
            // Resolve or reject the deferred based on success or failure of the reliable write.
            if (status == BluetoothGatt.GATT_SUCCESS) {
                executeReliableWrite.resolve(null);
            }
            else {
                executeReliableWrite.reject(status);
            }
        }
    }

    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        super.onDescriptorWrite(gatt, descriptor, status);
        DeferredObject<BluetoothGattDescriptor, Integer, Void> deferred = writeDescriptor.get(new DescriptorKey(descriptor));
        if (deferred != null) {
            // Resolve or reject the deferred based on success or failure of the descriptor write.
            if (status == BluetoothGatt.GATT_SUCCESS) {
                deferred.resolve(descriptor);
            }
            else {
                deferred.reject(status);
            }
        }
    }

    @Override
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
        super.onReadRemoteRssi(gatt, rssi, status);
        if (readRemoteRssi != null) {
            // Resolve or reject the deferred based on success or failure of the RSSI read.
            if (status == BluetoothGatt.GATT_SUCCESS) {
                readRemoteRssi.resolve(rssi);
            }
            else {
                readRemoteRssi.reject(status);
            }
        }
    }
}




Java Source Code List

betterbluetoothle.async.AsyncBluetoothGatt.java
betterbluetoothle.async.AsyncBluetoothLeScan.java
betterbluetoothle.services.UART.java