Darien Figueroa / Mbed 2 deprecated repo3

Dependencies:   mbed MAX14720 MAX30205 USBDevice

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers BluetoothLeService.java Source File

BluetoothLeService.java

00001 /*******************************************************************************
00002  * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
00003  * <p>
00004  * Permission is hereby granted, free of charge, to any person obtaining a
00005  * copy of this software and associated documentation files (the "Software"),
00006  * to deal in the Software without restriction, including without limitation
00007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00008  * and/or sell copies of the Software, and to permit persons to whom the
00009  * Software is furnished to do so, subject to the following conditions:
00010  * <p>
00011  * The above copyright notice and this permission notice shall be included
00012  * in all copies or substantial portions of the Software.
00013  * <p>
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
00015  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00016  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00017  * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
00018  * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00020  * OTHER DEALINGS IN THE SOFTWARE.
00021  * <p>
00022  * Except as contained in this notice, the name of Maxim Integrated
00023  * Products, Inc. shall not be used except as stated in the Maxim Integrated
00024  * Products, Inc. Branding Policy.
00025  * <p>
00026  * The mere transfer of this software does not imply any licenses
00027  * of trade secrets, proprietary technology, copyrights, patents,
00028  * trademarks, maskwork rights, or any other form of intellectual
00029  * property whatsoever. Maxim Integrated Products, Inc. retains all
00030  * ownership rights.
00031  * ******************************************************************************
00032  */
00033 package com.example.android.bluetoothlegatt;
00034 
00035 import android.app.Service;
00036 import android.bluetooth.BluetoothAdapter;
00037 import android.bluetooth.BluetoothDevice;
00038 import android.bluetooth.BluetoothGatt;
00039 import android.bluetooth.BluetoothGattCallback;
00040 import android.bluetooth.BluetoothGattCharacteristic;
00041 import android.bluetooth.BluetoothGattDescriptor;
00042 import android.bluetooth.BluetoothGattService;
00043 import android.bluetooth.BluetoothManager;
00044 import android.bluetooth.BluetoothProfile;
00045 import android.content.Context;
00046 import android.content.Intent;
00047 import android.os.Binder;
00048 import android.os.IBinder;
00049 import android.util.Log;
00050 
00051 import java.util.LinkedList;
00052 import java.util.List;
00053 import java.util.Queue;
00054 import java.util.UUID;
00055 
00056 /**
00057  * Service for managing connection and data communication with a GATT server hosted on a
00058  * given Bluetooth LE device.
00059  */
00060 public class BluetoothLeService extends Service {
00061     private final static String TAG = BluetoothLeService.class.getSimpleName();
00062 
00063     private BluetoothManager mBluetoothManager;
00064     private BluetoothAdapter mBluetoothAdapter;
00065     private String mBluetoothDeviceAddress;
00066     private BluetoothGatt mBluetoothGatt;
00067     private int mConnectionState = STATE_DISCONNECTED;
00068 
00069     private static final int STATE_DISCONNECTED = 0;
00070     private static final int STATE_CONNECTING = 1;
00071     private static final int STATE_CONNECTED = 2;
00072 
00073     /** ACTION_GATT_CONNECTED: connected to a GATT server.
00074      * ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
00075      * ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
00076      * ACTION_DATA_AVAILABLE: received data from the device.
00077      *                        this can be a result of read or notification operations.
00078      */
00079     public final static String ACTION_GATT_CONNECTED =
00080             "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
00081     public final static String ACTION_GATT_DISCONNECTED =
00082             "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
00083     public final static String ACTION_GATT_SERVICES_DISCOVERED =
00084             "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
00085     public final static String ACTION_DATA_AVAILABLE =
00086             "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
00087     public final static String EXTRA_DATA =
00088             "com.example.bluetooth.le.EXTRA_DATA";
00089     public final static String ACTION_CHARACTERISTIC_WRITE =
00090             "com.example.bluetooth.le.ACTION_CHARACTERISTIC_WRITE";
00091     public final static String EXTRA_DATA_RAW = "com.example.bluetooth.le.EXTRA_DATA_RAW";
00092     public final static String EXTRA_UUID_CHAR = "com.example.bluetooth.le.EXTRA_UUID_CHAR";
00093     public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
00094 
00095     // Implements callback methods for GATT events that the app cares about.  For example,
00096     // connection change and services discovered.
00097     private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
00098         @Override
00099         public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
00100             String intentAction;
00101             if (newState == BluetoothProfile.STATE_CONNECTED) {
00102                 intentAction = ACTION_GATT_CONNECTED;
00103                 mConnectionState = STATE_CONNECTED;
00104                 broadcastUpdate(intentAction);
00105                 Log.i(TAG, "Connected to GATT server.");
00106                 // Attempts to discover services after successful connection.
00107                 Log.i(TAG, "Attempting to start service discovery:" +
00108                         mBluetoothGatt.discoverServices());
00109 
00110             } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
00111                 intentAction = ACTION_GATT_DISCONNECTED;
00112                 mConnectionState = STATE_DISCONNECTED;
00113                 Log.i(TAG, "Disconnected from GATT server.");
00114                 broadcastUpdate(intentAction);
00115             }
00116         }
00117         
00118         /**
00119          * Called when the BLE services are discovered
00120          * @param gatt Gatt server of the device
00121          * @param status Success or fail status
00122          */
00123         @Override
00124         public void onServicesDiscovered(BluetoothGatt gatt, int status) {
00125             if (status == BluetoothGatt.GATT_SUCCESS) {
00126                 broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
00127             } else {
00128                 Log.w(TAG, "onServicesDiscovered received: " + status);
00129             }
00130         }
00131 
00132         /**
00133          * Called when a characteristic is read
00134          * @param gatt Gatt server of device
00135          * @param characteristic Characteristic that was read from
00136          * @param status Success or fail status
00137          */
00138         @Override
00139         public void onCharacteristicRead(BluetoothGatt gatt,
00140                                          BluetoothGattCharacteristic characteristic,
00141                                          int status) {
00142             if (status == BluetoothGatt.GATT_SUCCESS) {
00143                 broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
00144             }
00145         }
00146 
00147         /**
00148          * Called when a characteristic has changed
00149          * @param gatt Gatt server of device
00150          * @param characteristic Characteristic that has changed
00151          */
00152         @Override
00153         public void onCharacteristicChanged(BluetoothGatt gatt,
00154                                             BluetoothGattCharacteristic characteristic) {
00155             broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
00156         }
00157 
00158         /**
00159          * Called when a descriptor has been written to...
00160          * We will queue the next descriptor to write if there are any pending
00161          * @param gatt Gatt server of device
00162          * @param descriptor Descriptor that was written to
00163          * @param status Success or failure status
00164          */
00165         @Override
00166         public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
00167             super.onDescriptorWrite(gatt, descriptor, status);
00168 
00169             if (status == BluetoothGatt.GATT_SUCCESS) {
00170                 Log.d(TAG, "Callback: Wrote GATT Descriptor successfully.");
00171             } else {
00172                 Log.d(TAG, "Callback: Error writing GATT Descriptor: " + status);
00173             }
00174             descriptorWriteQueue.remove();  //pop the item that we just finishing writing
00175             //if there is more to write, do it!
00176             if (descriptorWriteQueue.size() > 0) {
00177                 mBluetoothGatt.writeDescriptor(descriptorWriteQueue.element());
00178             } else {
00179                 // we are done with everything in the queue
00180                 // send a callback event
00181                 Log.i(TAG, "broadcastUpdate, done with descriptor write queue");
00182             }
00183         }
00184     };
00185 
00186     /**
00187      * Broadcast an update to all registered to this service
00188      * @param action
00189      */
00190     private void broadcastUpdate(final String action) {
00191         final Intent intent = new Intent(action);
00192         sendBroadcast(intent);
00193     }
00194 
00195     /**
00196      * Broadcast an update that involves a characteristic and its data
00197      * @param action
00198      * @param characteristic
00199      */
00200     private void broadcastUpdate(final String action,
00201                                  final BluetoothGattCharacteristic characteristic) {
00202         final Intent intent = new Intent(action);
00203         intent.putExtra(EXTRA_UUID_CHAR, characteristic.getUuid().toString());
00204 
00205         // Always try to add the RAW value
00206         final byte[] data = characteristic.getValue();
00207         if (data != null && data.length > 0) {
00208             intent.putExtra(EXTRA_DATA_RAW, data);
00209         }
00210         sendBroadcast(intent);
00211     }
00212 
00213     /**
00214      * Return a binder for this system for android
00215      */
00216     public class LocalBinder extends Binder {
00217         BluetoothLeService getService() {
00218             return BluetoothLeService.this;
00219         }
00220     }
00221 
00222     /**
00223      * Bind using a specified intent
00224      * @param intent
00225      * @return
00226      */
00227     @Override
00228     public IBinder onBind(Intent intent) {
00229         return mBinder;
00230     }
00231 
00232     /**
00233      * Unbind this service
00234      * @param intent
00235      * @return
00236      */
00237     @Override
00238     public boolean onUnbind(Intent intent) {
00239         // After using a given device, you should make sure that BluetoothGatt.close() is called
00240         // such that resources are cleaned up properly.  In this particular example, close() is
00241         // invoked when the UI is disconnected from the Service.
00242         close();
00243         return super.onUnbind(intent);
00244     }
00245 
00246     /// define a local binder for this service
00247     private final IBinder mBinder = new LocalBinder();
00248 
00249     /**
00250      * Initializes a reference to the local Bluetooth adapter.
00251      * @return Return true if the initialization is successful.
00252      */
00253     public boolean initialize() {
00254         // For API level 18 and above, get a reference to BluetoothAdapter through
00255         // BluetoothManager.
00256         if (mBluetoothManager == null) {
00257             mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
00258             if (mBluetoothManager == null) {
00259                 Log.e(TAG, "Unable to initialize BluetoothManager.");
00260                 return false;
00261             }
00262         }
00263 
00264         mBluetoothAdapter = mBluetoothManager.getAdapter();
00265         if (mBluetoothAdapter == null) {
00266             Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
00267             return false;
00268         }
00269 
00270         return true;
00271     }
00272 
00273     /**
00274      * Connects to the GATT server hosted on the Bluetooth LE device.
00275      *
00276      * @param address The device address of the destination device.
00277      *
00278      * @return Return true if the connection is initiated successfully. The connection result
00279      *         is reported asynchronously through the
00280      *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
00281      *         callback.
00282      */
00283     public boolean connect(final String address) {
00284         if (mBluetoothAdapter == null || address == null) {
00285             Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
00286             return false;
00287         }
00288 
00289         // Previously connected device.  Try to reconnect.
00290         if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
00291                 && mBluetoothGatt != null) {
00292             Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
00293             if (mBluetoothGatt.connect()) {
00294                 mConnectionState = STATE_CONNECTING;
00295                 return true;
00296             } else {
00297                 return false;
00298             }
00299         }
00300 
00301         final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
00302         if (device == null) {
00303             Log.w(TAG, "Device not found.  Unable to connect.");
00304             return false;
00305         }
00306         // We want to directly connect to the device, so we are setting the autoConnect
00307         // parameter to false.
00308         mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
00309         Log.d(TAG, "Trying to create a new connection.");
00310         mBluetoothDeviceAddress = address;
00311         mConnectionState = STATE_CONNECTING;
00312         return true;
00313     }
00314 
00315     /**
00316      * Disconnects an existing connection or cancel a pending connection. The disconnection result
00317      * is reported asynchronously through the
00318      * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
00319      * callback.
00320      */
00321     public void disconnect() {
00322         if (mBluetoothAdapter == null || mBluetoothGatt == null) {
00323             Log.w(TAG, "BluetoothAdapter not initialized");
00324             return;
00325         }
00326         mBluetoothGatt.disconnect();
00327     }
00328 
00329     /**
00330      * After using a given BLE device, the app must call this method to ensure resources are
00331      * released properly.
00332      */
00333     public void close() {
00334         if (mBluetoothGatt == null) {
00335             return;
00336         }
00337         mBluetoothGatt.close();
00338         mBluetoothGatt = null;
00339     }
00340 
00341     /**
00342      * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported
00343      * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
00344      * callback.
00345      *
00346      * @param characteristic The characteristic to read from.
00347      */
00348     public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
00349         if (mBluetoothAdapter == null || mBluetoothGatt == null) {
00350             Log.w(TAG, "BluetoothAdapter not initialized");
00351             return;
00352         }
00353         mBluetoothGatt.readCharacteristic(characteristic);
00354     }
00355 
00356     /**
00357      * Enables or disables notification on a give characteristic.
00358      *
00359      * @param characteristic Characteristic to act on.
00360      * @param enabled If true, enable notification.  False otherwise.
00361      */
00362     public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
00363                                               boolean enabled) {
00364         if (mBluetoothAdapter == null || mBluetoothGatt == null) {
00365             Log.w(TAG, "BluetoothAdapter not initialized");
00366             return;
00367         }
00368         mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
00369 
00370     }
00371 
00372     /**
00373      * Retrieves a list of supported GATT services on the connected device. This should be
00374      * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
00375      *
00376      * @return A {@code List} of supported services.
00377      */
00378     public List<BluetoothGattService> getSupportedGattServices() {
00379         if (mBluetoothGatt == null) return null;
00380 
00381         return mBluetoothGatt.getServices();
00382     }
00383 
00384     /**
00385      * Write a characteristic by refering to it by its UUID and its service UUID
00386      * @param uuidService UUID of the service of the characteristic
00387      * @param uuidCharacteristic UUID of the characteristic to upate
00388      * @param data byte array of data to update in the characteristic
00389      * @return
00390      */
00391     public boolean writeCharacteristic(UUID uuidService, UUID uuidCharacteristic, byte[] data) {
00392         BluetoothGattService gattService = mBluetoothGatt.getService(uuidService);
00393         if (gattService == null) return false;
00394         BluetoothGattCharacteristic characteristic;
00395         characteristic = gattService.getCharacteristic(uuidCharacteristic);
00396         if (characteristic == null) {
00397             Log.e(TAG, "char not found!");
00398             return false;
00399         }
00400         //byte[] value = new byte[1];
00401         //value[0] = (byte) (21 & 0xFF);
00402         characteristic.setValue(data);
00403         boolean status = mBluetoothGatt.writeCharacteristic(characteristic);
00404         return status;
00405     }
00406 
00407     /** A queue used to allow write discriptors to be handled in a serial fashion...
00408      * waiting for each one to complete before starting the next
00409      */
00410     private Queue<BluetoothGattDescriptor> descriptorWriteQueue = new LinkedList<BluetoothGattDescriptor>();
00411 
00412     /**
00413      * Subscrible to a characteristic
00414      * @param uuidService Service that contains the characteristic
00415      * @param uuidCharacteristic The characteristic to subscribe to
00416      */
00417     public void Subscribe(UUID uuidService, UUID uuidCharacteristic) {
00418         if (mBluetoothGatt == null) return;
00419         BluetoothGattService gattService = mBluetoothGatt.getService(uuidService);
00420         if (gattService == null) return;
00421         BluetoothGattCharacteristic characteristic;
00422 
00423         characteristic = gattService.getCharacteristic(uuidCharacteristic);
00424         //setCharacteristicNotification(characteristic, true);
00425 
00426         setCharacteristicNotification(characteristic, true);
00427         //if (UUID_BLE_SHIELD_RX.equals(characteristic.getUuid())) {
00428         BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG));
00429         descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
00430         //WriteGattDescriptor(descriptor);
00431 
00432         //put the descriptor into the write queue
00433         descriptorWriteQueue.add(descriptor);
00434         //if there is only 1 item in the queue, then write it.  If more than 1, we handle asynchronously in the callback above
00435         if (descriptorWriteQueue.size() == 1) {
00436             mBluetoothGatt.writeDescriptor(descriptor);
00437         }
00438     }
00439 
00440     /**
00441      * Unsubscribe to a characteristic
00442      * @param uuidService Service that contains the characteristic
00443      * @param uuidCharacteristic The characteristic to unsubscribe from
00444      */
00445     public void Unsubscribe(UUID uuidService, UUID uuidCharacteristic) {
00446         if (mBluetoothGatt == null) return;
00447         BluetoothGattService gattService = mBluetoothGatt.getService(uuidService);
00448         if (gattService == null) return;
00449         BluetoothGattCharacteristic characteristic;
00450 
00451         characteristic = gattService.getCharacteristic(uuidCharacteristic);
00452         //setCharacteristicNotification(characteristic, true);
00453 
00454         setCharacteristicNotification(characteristic, false);
00455         //if (UUID_BLE_SHIELD_RX.equals(characteristic.getUuid())) {
00456         BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG));
00457         descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
00458         //WriteGattDescriptor(descriptor);
00459 
00460         //put the descriptor into the write queue
00461         descriptorWriteQueue.add(descriptor);
00462         //if there is only 1 item in the queue, then write it.  If more than 1, we handle asynchronously in the callback above
00463         if (descriptorWriteQueue.size() == 1) {
00464             mBluetoothGatt.writeDescriptor(descriptor);
00465         }
00466     }
00467 
00468 }