Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed MAX14720 MAX30205 USBDevice
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 }
Generated on Thu Jul 28 2022 18:07:13 by
1.7.2