Back to project page Adafruit_Android_BLE_UART.
The source code is released under:
MIT License
If you think the Android project Adafruit_Android_BLE_UART listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.adafruit.bleuart; /* ww w.j a va2s . c o m*/ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.content.Context; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Queue; import java.util.UUID; import java.util.WeakHashMap; import java.lang.String; import java.util.concurrent.ConcurrentLinkedQueue; public class BluetoothLeUart extends BluetoothGattCallback implements BluetoothAdapter.LeScanCallback { // UUIDs for UART service and associated characteristics. public static UUID UART_UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E"); public static UUID TX_UUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"); public static UUID RX_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"); // UUID for the UART BTLE client characteristic which is necessary for notifications. public static UUID CLIENT_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); // UUIDs for the Device Information service and associated characeristics. public static UUID DIS_UUID = UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb"); public static UUID DIS_MANUF_UUID = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb"); public static UUID DIS_MODEL_UUID = UUID.fromString("00002a24-0000-1000-8000-00805f9b34fb"); public static UUID DIS_HWREV_UUID = UUID.fromString("00002a26-0000-1000-8000-00805f9b34fb"); public static UUID DIS_SWREV_UUID = UUID.fromString("00002a28-0000-1000-8000-00805f9b34fb"); // Internal UART state. private Context context; private WeakHashMap<Callback, Object> callbacks; private BluetoothAdapter adapter; private BluetoothGatt gatt; private BluetoothGattCharacteristic tx; private BluetoothGattCharacteristic rx; private boolean connectFirst; private boolean writeInProgress; // Flag to indicate a write is currently in progress // Device Information state. private BluetoothGattCharacteristic disManuf; private BluetoothGattCharacteristic disModel; private BluetoothGattCharacteristic disHWRev; private BluetoothGattCharacteristic disSWRev; private boolean disAvailable; // Queues for characteristic read (synchronous) private Queue<BluetoothGattCharacteristic> readQueue; // Interface for a BluetoothLeUart client to be notified of UART actions. public interface Callback { public void onConnected(BluetoothLeUart uart); public void onConnectFailed(BluetoothLeUart uart); public void onDisconnected(BluetoothLeUart uart); public void onReceive(BluetoothLeUart uart, BluetoothGattCharacteristic rx); public void onDeviceFound(BluetoothDevice device); public void onDeviceInfoAvailable(); } public BluetoothLeUart(Context context) { super(); this.context = context; this.callbacks = new WeakHashMap<Callback, Object>(); this.adapter = BluetoothAdapter.getDefaultAdapter(); this.gatt = null; this.tx = null; this.rx = null; this.disManuf = null; this.disModel = null; this.disHWRev = null; this.disSWRev = null; this.disAvailable = false; this.connectFirst = false; this.writeInProgress = false; this.readQueue = new ConcurrentLinkedQueue<BluetoothGattCharacteristic>(); } // Return instance of BluetoothGatt. public BluetoothGatt getGatt() { return gatt; } // Return true if connected to UART device, false otherwise. public boolean isConnected() { return (tx != null && rx != null); } public String getDeviceInfo() { if (tx == null || !disAvailable ) { // Do nothing if there is no connection. return ""; } StringBuilder sb = new StringBuilder(); sb.append("Manufacturer : " + disManuf.getStringValue(0) + "\n"); sb.append("Model : " + disModel.getStringValue(0) + "\n"); sb.append("Firmware : " + disSWRev.getStringValue(0) + "\n"); return sb.toString(); }; public boolean deviceInfoAvailable() { return disAvailable; } // Send data to connected UART device. public void send(byte[] data) { if (tx == null || data == null || data.length == 0) { // Do nothing if there is no connection or message to send. return; } // Update TX characteristic value. Note the setValue overload that takes a byte array must be used. tx.setValue(data); writeInProgress = true; // Set the write in progress flag gatt.writeCharacteristic(tx); // ToDo: Update to include a timeout in case this goes into the weeds while (writeInProgress); // Wait for the flag to clear in onCharacteristicWrite } // Send data to connected UART device. public void send(String data) { if (data != null && !data.isEmpty()) { send(data.getBytes(Charset.forName("UTF-8"))); } } // Register the specified callback to receive UART callbacks. public void registerCallback(Callback callback) { callbacks.put(callback, null); } // Unregister the specified callback. public void unregisterCallback(Callback callback) { callbacks.remove(callback); } // Disconnect to a device if currently connected. public void disconnect() { if (gatt != null) { gatt.disconnect(); } gatt = null; tx = null; rx = null; } // Stop any in progress UART device scan. public void stopScan() { if (adapter != null) { adapter.stopLeScan(this); } } // Start scanning for BLE UART devices. Registered callback's onDeviceFound method will be called // when devices are found during scanning. public void startScan() { if (adapter != null) { adapter.startLeScan(this); } } // Connect to the first available UART device. public void connectFirstAvailable() { // Disconnect to any connected device. disconnect(); // Stop any in progress device scan. stopScan(); // Start scan and connect to first available device. connectFirst = true; startScan(); } // Handlers for BluetoothGatt and LeScan events. @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (newState == BluetoothGatt.STATE_CONNECTED) { if (status == BluetoothGatt.GATT_SUCCESS) { // Connected to device, start discovering services. if (!gatt.discoverServices()) { // Error starting service discovery. connectFailure(); } } else { // Error connecting to device. connectFailure(); } } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { // Disconnected, notify callbacks of disconnection. rx = null; tx = null; notifyOnDisconnected(this); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); // Notify connection failure if service discovery failed. if (status == BluetoothGatt.GATT_FAILURE) { connectFailure(); return; } // Save reference to each UART characteristic. tx = gatt.getService(UART_UUID).getCharacteristic(TX_UUID); rx = gatt.getService(UART_UUID).getCharacteristic(RX_UUID); // Save reference to each DIS characteristic. disManuf = gatt.getService(DIS_UUID).getCharacteristic(DIS_MANUF_UUID); disModel = gatt.getService(DIS_UUID).getCharacteristic(DIS_MODEL_UUID); disHWRev = gatt.getService(DIS_UUID).getCharacteristic(DIS_HWREV_UUID); disSWRev = gatt.getService(DIS_UUID).getCharacteristic(DIS_SWREV_UUID); // Add device information characteristics to the read queue // These need to be queued because we have to wait for the response to the first // read request before a second one can be processed (which makes you wonder why they // implemented this with async logic to begin with???) readQueue.offer(disManuf); readQueue.offer(disModel); readQueue.offer(disHWRev); readQueue.offer(disSWRev); // Request a dummy read to get the device information queue going gatt.readCharacteristic(disManuf); // Setup notifications on RX characteristic changes (i.e. data received). // First call setCharacteristicNotification to enable notification. if (!gatt.setCharacteristicNotification(rx, true)) { // Stop if the characteristic notification setup failed. connectFailure(); return; } // Next update the RX characteristic's client descriptor to enable notifications. BluetoothGattDescriptor desc = rx.getDescriptor(CLIENT_UUID); if (desc == null) { // Stop if the RX characteristic has no client descriptor. connectFailure(); return; } desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); if (!gatt.writeDescriptor(desc)) { // Stop if the client descriptor could not be written. connectFailure(); return; } // Notify of connection completion. notifyOnConnected(this); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); notifyOnReceive(this, characteristic); } @Override public void onCharacteristicRead (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { //Log.w("DIS", characteristic.getStringValue(0)); // Check if there is anything left in the queue BluetoothGattCharacteristic nextRequest = readQueue.poll(); if(nextRequest != null){ // Send a read request for the next item in the queue gatt.readCharacteristic(nextRequest); } else { // We've reached the end of the queue disAvailable = true; notifyOnDeviceInfoAvailable(); } } else { //Log.w("DIS", "Failed reading characteristic " + characteristic.getUuid().toString()); } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { // Log.d(TAG,"Characteristic write successful"); } writeInProgress = false; } @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { // Stop if the device doesn't have the UART service. if (!parseUUIDs(scanRecord).contains(UART_UUID)) { return; } // Notify registered callbacks of found device. notifyOnDeviceFound(device); // Connect to first found device if required. if (connectFirst) { // Stop scanning for devices. stopScan(); // Prevent connections to future found devices. connectFirst = false; // Connect to device. gatt = device.connectGatt(context, true, this); } } // Private functions to simplify the notification of all callbacks of a certain event. private void notifyOnConnected(BluetoothLeUart uart) { for (Callback cb : callbacks.keySet()) { if (cb != null) { cb.onConnected(uart); } } } private void notifyOnConnectFailed(BluetoothLeUart uart) { for (Callback cb : callbacks.keySet()) { if (cb != null) { cb.onConnectFailed(uart); } } } private void notifyOnDisconnected(BluetoothLeUart uart) { for (Callback cb : callbacks.keySet()) { if (cb != null) { cb.onDisconnected(uart); } } } private void notifyOnReceive(BluetoothLeUart uart, BluetoothGattCharacteristic rx) { for (Callback cb : callbacks.keySet()) { if (cb != null ) { cb.onReceive(uart, rx); } } } private void notifyOnDeviceFound(BluetoothDevice device) { for (Callback cb : callbacks.keySet()) { if (cb != null) { cb.onDeviceFound(device); } } } private void notifyOnDeviceInfoAvailable() { for (Callback cb : callbacks.keySet()) { if (cb != null) { cb.onDeviceInfoAvailable(); } } } // Notify callbacks of connection failure, and reset connection state. private void connectFailure() { rx = null; tx = null; notifyOnConnectFailed(this); } // Filtering by custom UUID is broken in Android 4.3 and 4.4, see: // http://stackoverflow.com/questions/18019161/startlescan-with-128-bit-uuids-doesnt-work-on-native-android-ble-implementation?noredirect=1#comment27879874_18019161 // This is a workaround function from the SO thread to manually parse advertisement data. private List<UUID> parseUUIDs(final byte[] advertisedData) { List<UUID> uuids = new ArrayList<UUID>(); int offset = 0; while (offset < (advertisedData.length - 2)) { int len = advertisedData[offset++]; if (len == 0) break; int type = advertisedData[offset++]; switch (type) { case 0x02: // Partial list of 16-bit UUIDs case 0x03: // Complete list of 16-bit UUIDs while (len > 1) { int uuid16 = advertisedData[offset++]; uuid16 += (advertisedData[offset++] << 8); len -= 2; uuids.add(UUID.fromString(String.format("%08x-0000-1000-8000-00805f9b34fb", uuid16))); } break; case 0x06:// Partial list of 128-bit UUIDs case 0x07:// Complete list of 128-bit UUIDs // Loop through the advertised 128-bit UUID's. while (len >= 16) { try { // Wrap the advertised bits and order them. ByteBuffer buffer = ByteBuffer.wrap(advertisedData, offset++, 16).order(ByteOrder.LITTLE_ENDIAN); long mostSignificantBit = buffer.getLong(); long leastSignificantBit = buffer.getLong(); uuids.add(new UUID(leastSignificantBit, mostSignificantBit)); } catch (IndexOutOfBoundsException e) { // Defensive programming. //Log.e(LOG_TAG, e.toString()); continue; } finally { // Move the offset to read the next uuid. offset += 15; len -= 16; } } break; default: offset += (len - 1); break; } } return uuids; } }