1*90c8c64dSAndroid Build Coastguard Worker /* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2013 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker * 4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker * 8*90c8c64dSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker * 10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License. 15*90c8c64dSAndroid Build Coastguard Worker */ 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Worker package com.example.android.bluetoothlegatt; 18*90c8c64dSAndroid Build Coastguard Worker 19*90c8c64dSAndroid Build Coastguard Worker import android.app.Service; 20*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothAdapter; 21*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothDevice; 22*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothGatt; 23*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothGattCallback; 24*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothGattCharacteristic; 25*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothGattDescriptor; 26*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothGattService; 27*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothManager; 28*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothProfile; 29*90c8c64dSAndroid Build Coastguard Worker import android.content.Context; 30*90c8c64dSAndroid Build Coastguard Worker import android.content.Intent; 31*90c8c64dSAndroid Build Coastguard Worker import android.os.Binder; 32*90c8c64dSAndroid Build Coastguard Worker import android.os.IBinder; 33*90c8c64dSAndroid Build Coastguard Worker import android.util.Log; 34*90c8c64dSAndroid Build Coastguard Worker 35*90c8c64dSAndroid Build Coastguard Worker import java.util.List; 36*90c8c64dSAndroid Build Coastguard Worker import java.util.UUID; 37*90c8c64dSAndroid Build Coastguard Worker 38*90c8c64dSAndroid Build Coastguard Worker /** 39*90c8c64dSAndroid Build Coastguard Worker * Service for managing connection and data communication with a GATT server hosted on a 40*90c8c64dSAndroid Build Coastguard Worker * given Bluetooth LE device. 41*90c8c64dSAndroid Build Coastguard Worker */ 42*90c8c64dSAndroid Build Coastguard Worker public class BluetoothLeService extends Service { 43*90c8c64dSAndroid Build Coastguard Worker private final static String TAG = BluetoothLeService.class.getSimpleName(); 44*90c8c64dSAndroid Build Coastguard Worker 45*90c8c64dSAndroid Build Coastguard Worker private BluetoothManager mBluetoothManager; 46*90c8c64dSAndroid Build Coastguard Worker private BluetoothAdapter mBluetoothAdapter; 47*90c8c64dSAndroid Build Coastguard Worker private String mBluetoothDeviceAddress; 48*90c8c64dSAndroid Build Coastguard Worker private BluetoothGatt mBluetoothGatt; 49*90c8c64dSAndroid Build Coastguard Worker private int mConnectionState = STATE_DISCONNECTED; 50*90c8c64dSAndroid Build Coastguard Worker 51*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_DISCONNECTED = 0; 52*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_CONNECTING = 1; 53*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_CONNECTED = 2; 54*90c8c64dSAndroid Build Coastguard Worker 55*90c8c64dSAndroid Build Coastguard Worker public final static String ACTION_GATT_CONNECTED = 56*90c8c64dSAndroid Build Coastguard Worker "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; 57*90c8c64dSAndroid Build Coastguard Worker public final static String ACTION_GATT_DISCONNECTED = 58*90c8c64dSAndroid Build Coastguard Worker "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; 59*90c8c64dSAndroid Build Coastguard Worker public final static String ACTION_GATT_SERVICES_DISCOVERED = 60*90c8c64dSAndroid Build Coastguard Worker "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; 61*90c8c64dSAndroid Build Coastguard Worker public final static String ACTION_DATA_AVAILABLE = 62*90c8c64dSAndroid Build Coastguard Worker "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; 63*90c8c64dSAndroid Build Coastguard Worker public final static String EXTRA_DATA = 64*90c8c64dSAndroid Build Coastguard Worker "com.example.bluetooth.le.EXTRA_DATA"; 65*90c8c64dSAndroid Build Coastguard Worker 66*90c8c64dSAndroid Build Coastguard Worker public final static UUID UUID_HEART_RATE_MEASUREMENT = 67*90c8c64dSAndroid Build Coastguard Worker UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT); 68*90c8c64dSAndroid Build Coastguard Worker 69*90c8c64dSAndroid Build Coastguard Worker // Implements callback methods for GATT events that the app cares about. For example, 70*90c8c64dSAndroid Build Coastguard Worker // connection change and services discovered. 71*90c8c64dSAndroid Build Coastguard Worker private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { 72*90c8c64dSAndroid Build Coastguard Worker @Override 73*90c8c64dSAndroid Build Coastguard Worker public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 74*90c8c64dSAndroid Build Coastguard Worker String intentAction; 75*90c8c64dSAndroid Build Coastguard Worker if (newState == BluetoothProfile.STATE_CONNECTED) { 76*90c8c64dSAndroid Build Coastguard Worker intentAction = ACTION_GATT_CONNECTED; 77*90c8c64dSAndroid Build Coastguard Worker mConnectionState = STATE_CONNECTED; 78*90c8c64dSAndroid Build Coastguard Worker broadcastUpdate(intentAction); 79*90c8c64dSAndroid Build Coastguard Worker Log.i(TAG, "Connected to GATT server."); 80*90c8c64dSAndroid Build Coastguard Worker // Attempts to discover services after successful connection. 81*90c8c64dSAndroid Build Coastguard Worker Log.i(TAG, "Attempting to start service discovery:" + 82*90c8c64dSAndroid Build Coastguard Worker mBluetoothGatt.discoverServices()); 83*90c8c64dSAndroid Build Coastguard Worker 84*90c8c64dSAndroid Build Coastguard Worker } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { 85*90c8c64dSAndroid Build Coastguard Worker intentAction = ACTION_GATT_DISCONNECTED; 86*90c8c64dSAndroid Build Coastguard Worker mConnectionState = STATE_DISCONNECTED; 87*90c8c64dSAndroid Build Coastguard Worker Log.i(TAG, "Disconnected from GATT server."); 88*90c8c64dSAndroid Build Coastguard Worker broadcastUpdate(intentAction); 89*90c8c64dSAndroid Build Coastguard Worker } 90*90c8c64dSAndroid Build Coastguard Worker } 91*90c8c64dSAndroid Build Coastguard Worker 92*90c8c64dSAndroid Build Coastguard Worker @Override 93*90c8c64dSAndroid Build Coastguard Worker public void onServicesDiscovered(BluetoothGatt gatt, int status) { 94*90c8c64dSAndroid Build Coastguard Worker if (status == BluetoothGatt.GATT_SUCCESS) { 95*90c8c64dSAndroid Build Coastguard Worker broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); 96*90c8c64dSAndroid Build Coastguard Worker } else { 97*90c8c64dSAndroid Build Coastguard Worker Log.w(TAG, "onServicesDiscovered received: " + status); 98*90c8c64dSAndroid Build Coastguard Worker } 99*90c8c64dSAndroid Build Coastguard Worker } 100*90c8c64dSAndroid Build Coastguard Worker 101*90c8c64dSAndroid Build Coastguard Worker @Override 102*90c8c64dSAndroid Build Coastguard Worker public void onCharacteristicRead(BluetoothGatt gatt, 103*90c8c64dSAndroid Build Coastguard Worker BluetoothGattCharacteristic characteristic, 104*90c8c64dSAndroid Build Coastguard Worker int status) { 105*90c8c64dSAndroid Build Coastguard Worker if (status == BluetoothGatt.GATT_SUCCESS) { 106*90c8c64dSAndroid Build Coastguard Worker broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); 107*90c8c64dSAndroid Build Coastguard Worker } 108*90c8c64dSAndroid Build Coastguard Worker } 109*90c8c64dSAndroid Build Coastguard Worker 110*90c8c64dSAndroid Build Coastguard Worker @Override 111*90c8c64dSAndroid Build Coastguard Worker public void onCharacteristicChanged(BluetoothGatt gatt, 112*90c8c64dSAndroid Build Coastguard Worker BluetoothGattCharacteristic characteristic) { 113*90c8c64dSAndroid Build Coastguard Worker broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); 114*90c8c64dSAndroid Build Coastguard Worker } 115*90c8c64dSAndroid Build Coastguard Worker }; 116*90c8c64dSAndroid Build Coastguard Worker broadcastUpdate(final String action)117*90c8c64dSAndroid Build Coastguard Worker private void broadcastUpdate(final String action) { 118*90c8c64dSAndroid Build Coastguard Worker final Intent intent = new Intent(action); 119*90c8c64dSAndroid Build Coastguard Worker sendBroadcast(intent); 120*90c8c64dSAndroid Build Coastguard Worker } 121*90c8c64dSAndroid Build Coastguard Worker broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic)122*90c8c64dSAndroid Build Coastguard Worker private void broadcastUpdate(final String action, 123*90c8c64dSAndroid Build Coastguard Worker final BluetoothGattCharacteristic characteristic) { 124*90c8c64dSAndroid Build Coastguard Worker final Intent intent = new Intent(action); 125*90c8c64dSAndroid Build Coastguard Worker 126*90c8c64dSAndroid Build Coastguard Worker // This is special handling for the Heart Rate Measurement profile. Data parsing is 127*90c8c64dSAndroid Build Coastguard Worker // carried out as per profile specifications: 128*90c8c64dSAndroid Build Coastguard Worker // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml 129*90c8c64dSAndroid Build Coastguard Worker if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { 130*90c8c64dSAndroid Build Coastguard Worker int flag = characteristic.getProperties(); 131*90c8c64dSAndroid Build Coastguard Worker int format = -1; 132*90c8c64dSAndroid Build Coastguard Worker if ((flag & 0x01) != 0) { 133*90c8c64dSAndroid Build Coastguard Worker format = BluetoothGattCharacteristic.FORMAT_UINT16; 134*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "Heart rate format UINT16."); 135*90c8c64dSAndroid Build Coastguard Worker } else { 136*90c8c64dSAndroid Build Coastguard Worker format = BluetoothGattCharacteristic.FORMAT_UINT8; 137*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "Heart rate format UINT8."); 138*90c8c64dSAndroid Build Coastguard Worker } 139*90c8c64dSAndroid Build Coastguard Worker final int heartRate = characteristic.getIntValue(format, 1); 140*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, String.format("Received heart rate: %d", heartRate)); 141*90c8c64dSAndroid Build Coastguard Worker intent.putExtra(EXTRA_DATA, String.valueOf(heartRate)); 142*90c8c64dSAndroid Build Coastguard Worker } else { 143*90c8c64dSAndroid Build Coastguard Worker // For all other profiles, writes the data formatted in HEX. 144*90c8c64dSAndroid Build Coastguard Worker final byte[] data = characteristic.getValue(); 145*90c8c64dSAndroid Build Coastguard Worker if (data != null && data.length > 0) { 146*90c8c64dSAndroid Build Coastguard Worker final StringBuilder stringBuilder = new StringBuilder(data.length); 147*90c8c64dSAndroid Build Coastguard Worker for(byte byteChar : data) 148*90c8c64dSAndroid Build Coastguard Worker stringBuilder.append(String.format("%02X ", byteChar)); 149*90c8c64dSAndroid Build Coastguard Worker intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString()); 150*90c8c64dSAndroid Build Coastguard Worker } 151*90c8c64dSAndroid Build Coastguard Worker } 152*90c8c64dSAndroid Build Coastguard Worker sendBroadcast(intent); 153*90c8c64dSAndroid Build Coastguard Worker } 154*90c8c64dSAndroid Build Coastguard Worker 155*90c8c64dSAndroid Build Coastguard Worker public class LocalBinder extends Binder { getService()156*90c8c64dSAndroid Build Coastguard Worker BluetoothLeService getService() { 157*90c8c64dSAndroid Build Coastguard Worker return BluetoothLeService.this; 158*90c8c64dSAndroid Build Coastguard Worker } 159*90c8c64dSAndroid Build Coastguard Worker } 160*90c8c64dSAndroid Build Coastguard Worker 161*90c8c64dSAndroid Build Coastguard Worker @Override onBind(Intent intent)162*90c8c64dSAndroid Build Coastguard Worker public IBinder onBind(Intent intent) { 163*90c8c64dSAndroid Build Coastguard Worker return mBinder; 164*90c8c64dSAndroid Build Coastguard Worker } 165*90c8c64dSAndroid Build Coastguard Worker 166*90c8c64dSAndroid Build Coastguard Worker @Override onUnbind(Intent intent)167*90c8c64dSAndroid Build Coastguard Worker public boolean onUnbind(Intent intent) { 168*90c8c64dSAndroid Build Coastguard Worker // After using a given device, you should make sure that BluetoothGatt.close() is called 169*90c8c64dSAndroid Build Coastguard Worker // such that resources are cleaned up properly. In this particular example, close() is 170*90c8c64dSAndroid Build Coastguard Worker // invoked when the UI is disconnected from the Service. 171*90c8c64dSAndroid Build Coastguard Worker close(); 172*90c8c64dSAndroid Build Coastguard Worker return super.onUnbind(intent); 173*90c8c64dSAndroid Build Coastguard Worker } 174*90c8c64dSAndroid Build Coastguard Worker 175*90c8c64dSAndroid Build Coastguard Worker private final IBinder mBinder = new LocalBinder(); 176*90c8c64dSAndroid Build Coastguard Worker 177*90c8c64dSAndroid Build Coastguard Worker /** 178*90c8c64dSAndroid Build Coastguard Worker * Initializes a reference to the local Bluetooth adapter. 179*90c8c64dSAndroid Build Coastguard Worker * 180*90c8c64dSAndroid Build Coastguard Worker * @return Return true if the initialization is successful. 181*90c8c64dSAndroid Build Coastguard Worker */ initialize()182*90c8c64dSAndroid Build Coastguard Worker public boolean initialize() { 183*90c8c64dSAndroid Build Coastguard Worker // For API level 18 and above, get a reference to BluetoothAdapter through 184*90c8c64dSAndroid Build Coastguard Worker // BluetoothManager. 185*90c8c64dSAndroid Build Coastguard Worker if (mBluetoothManager == null) { 186*90c8c64dSAndroid Build Coastguard Worker mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 187*90c8c64dSAndroid Build Coastguard Worker if (mBluetoothManager == null) { 188*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Unable to initialize BluetoothManager."); 189*90c8c64dSAndroid Build Coastguard Worker return false; 190*90c8c64dSAndroid Build Coastguard Worker } 191*90c8c64dSAndroid Build Coastguard Worker } 192*90c8c64dSAndroid Build Coastguard Worker 193*90c8c64dSAndroid Build Coastguard Worker mBluetoothAdapter = mBluetoothManager.getAdapter(); 194*90c8c64dSAndroid Build Coastguard Worker if (mBluetoothAdapter == null) { 195*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Unable to obtain a BluetoothAdapter."); 196*90c8c64dSAndroid Build Coastguard Worker return false; 197*90c8c64dSAndroid Build Coastguard Worker } 198*90c8c64dSAndroid Build Coastguard Worker 199*90c8c64dSAndroid Build Coastguard Worker return true; 200*90c8c64dSAndroid Build Coastguard Worker } 201*90c8c64dSAndroid Build Coastguard Worker 202*90c8c64dSAndroid Build Coastguard Worker /** 203*90c8c64dSAndroid Build Coastguard Worker * Connects to the GATT server hosted on the Bluetooth LE device. 204*90c8c64dSAndroid Build Coastguard Worker * 205*90c8c64dSAndroid Build Coastguard Worker * @param address The device address of the destination device. 206*90c8c64dSAndroid Build Coastguard Worker * 207*90c8c64dSAndroid Build Coastguard Worker * @return Return true if the connection is initiated successfully. The connection result 208*90c8c64dSAndroid Build Coastguard Worker * is reported asynchronously through the 209*90c8c64dSAndroid Build Coastguard Worker * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} 210*90c8c64dSAndroid Build Coastguard Worker * callback. 211*90c8c64dSAndroid Build Coastguard Worker */ connect(final String address)212*90c8c64dSAndroid Build Coastguard Worker public boolean connect(final String address) { 213*90c8c64dSAndroid Build Coastguard Worker if (mBluetoothAdapter == null || address == null) { 214*90c8c64dSAndroid Build Coastguard Worker Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); 215*90c8c64dSAndroid Build Coastguard Worker return false; 216*90c8c64dSAndroid Build Coastguard Worker } 217*90c8c64dSAndroid Build Coastguard Worker 218*90c8c64dSAndroid Build Coastguard Worker // Previously connected device. Try to reconnect. 219*90c8c64dSAndroid Build Coastguard Worker if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) 220*90c8c64dSAndroid Build Coastguard Worker && mBluetoothGatt != null) { 221*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection."); 222*90c8c64dSAndroid Build Coastguard Worker if (mBluetoothGatt.connect()) { 223*90c8c64dSAndroid Build Coastguard Worker mConnectionState = STATE_CONNECTING; 224*90c8c64dSAndroid Build Coastguard Worker return true; 225*90c8c64dSAndroid Build Coastguard Worker } else { 226*90c8c64dSAndroid Build Coastguard Worker return false; 227*90c8c64dSAndroid Build Coastguard Worker } 228*90c8c64dSAndroid Build Coastguard Worker } 229*90c8c64dSAndroid Build Coastguard Worker 230*90c8c64dSAndroid Build Coastguard Worker final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); 231*90c8c64dSAndroid Build Coastguard Worker if (device == null) { 232*90c8c64dSAndroid Build Coastguard Worker Log.w(TAG, "Device not found. Unable to connect."); 233*90c8c64dSAndroid Build Coastguard Worker return false; 234*90c8c64dSAndroid Build Coastguard Worker } 235*90c8c64dSAndroid Build Coastguard Worker // We want to directly connect to the device, so we are setting the autoConnect 236*90c8c64dSAndroid Build Coastguard Worker // parameter to false. 237*90c8c64dSAndroid Build Coastguard Worker mBluetoothGatt = device.connectGatt(this, false, mGattCallback); 238*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "Trying to create a new connection."); 239*90c8c64dSAndroid Build Coastguard Worker mBluetoothDeviceAddress = address; 240*90c8c64dSAndroid Build Coastguard Worker mConnectionState = STATE_CONNECTING; 241*90c8c64dSAndroid Build Coastguard Worker return true; 242*90c8c64dSAndroid Build Coastguard Worker } 243*90c8c64dSAndroid Build Coastguard Worker 244*90c8c64dSAndroid Build Coastguard Worker /** 245*90c8c64dSAndroid Build Coastguard Worker * Disconnects an existing connection or cancel a pending connection. The disconnection result 246*90c8c64dSAndroid Build Coastguard Worker * is reported asynchronously through the 247*90c8c64dSAndroid Build Coastguard Worker * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} 248*90c8c64dSAndroid Build Coastguard Worker * callback. 249*90c8c64dSAndroid Build Coastguard Worker */ disconnect()250*90c8c64dSAndroid Build Coastguard Worker public void disconnect() { 251*90c8c64dSAndroid Build Coastguard Worker if (mBluetoothAdapter == null || mBluetoothGatt == null) { 252*90c8c64dSAndroid Build Coastguard Worker Log.w(TAG, "BluetoothAdapter not initialized"); 253*90c8c64dSAndroid Build Coastguard Worker return; 254*90c8c64dSAndroid Build Coastguard Worker } 255*90c8c64dSAndroid Build Coastguard Worker mBluetoothGatt.disconnect(); 256*90c8c64dSAndroid Build Coastguard Worker } 257*90c8c64dSAndroid Build Coastguard Worker 258*90c8c64dSAndroid Build Coastguard Worker /** 259*90c8c64dSAndroid Build Coastguard Worker * After using a given BLE device, the app must call this method to ensure resources are 260*90c8c64dSAndroid Build Coastguard Worker * released properly. 261*90c8c64dSAndroid Build Coastguard Worker */ close()262*90c8c64dSAndroid Build Coastguard Worker public void close() { 263*90c8c64dSAndroid Build Coastguard Worker if (mBluetoothGatt == null) { 264*90c8c64dSAndroid Build Coastguard Worker return; 265*90c8c64dSAndroid Build Coastguard Worker } 266*90c8c64dSAndroid Build Coastguard Worker mBluetoothGatt.close(); 267*90c8c64dSAndroid Build Coastguard Worker mBluetoothGatt = null; 268*90c8c64dSAndroid Build Coastguard Worker } 269*90c8c64dSAndroid Build Coastguard Worker 270*90c8c64dSAndroid Build Coastguard Worker /** 271*90c8c64dSAndroid Build Coastguard Worker * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported 272*90c8c64dSAndroid Build Coastguard Worker * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} 273*90c8c64dSAndroid Build Coastguard Worker * callback. 274*90c8c64dSAndroid Build Coastguard Worker * 275*90c8c64dSAndroid Build Coastguard Worker * @param characteristic The characteristic to read from. 276*90c8c64dSAndroid Build Coastguard Worker */ readCharacteristic(BluetoothGattCharacteristic characteristic)277*90c8c64dSAndroid Build Coastguard Worker public void readCharacteristic(BluetoothGattCharacteristic characteristic) { 278*90c8c64dSAndroid Build Coastguard Worker if (mBluetoothAdapter == null || mBluetoothGatt == null) { 279*90c8c64dSAndroid Build Coastguard Worker Log.w(TAG, "BluetoothAdapter not initialized"); 280*90c8c64dSAndroid Build Coastguard Worker return; 281*90c8c64dSAndroid Build Coastguard Worker } 282*90c8c64dSAndroid Build Coastguard Worker mBluetoothGatt.readCharacteristic(characteristic); 283*90c8c64dSAndroid Build Coastguard Worker } 284*90c8c64dSAndroid Build Coastguard Worker 285*90c8c64dSAndroid Build Coastguard Worker /** 286*90c8c64dSAndroid Build Coastguard Worker * Enables or disables notification on a give characteristic. 287*90c8c64dSAndroid Build Coastguard Worker * 288*90c8c64dSAndroid Build Coastguard Worker * @param characteristic Characteristic to act on. 289*90c8c64dSAndroid Build Coastguard Worker * @param enabled If true, enable notification. False otherwise. 290*90c8c64dSAndroid Build Coastguard Worker */ setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled)291*90c8c64dSAndroid Build Coastguard Worker public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, 292*90c8c64dSAndroid Build Coastguard Worker boolean enabled) { 293*90c8c64dSAndroid Build Coastguard Worker if (mBluetoothAdapter == null || mBluetoothGatt == null) { 294*90c8c64dSAndroid Build Coastguard Worker Log.w(TAG, "BluetoothAdapter not initialized"); 295*90c8c64dSAndroid Build Coastguard Worker return; 296*90c8c64dSAndroid Build Coastguard Worker } 297*90c8c64dSAndroid Build Coastguard Worker mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); 298*90c8c64dSAndroid Build Coastguard Worker 299*90c8c64dSAndroid Build Coastguard Worker // This is specific to Heart Rate Measurement. 300*90c8c64dSAndroid Build Coastguard Worker if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { 301*90c8c64dSAndroid Build Coastguard Worker BluetoothGattDescriptor descriptor = characteristic.getDescriptor( 302*90c8c64dSAndroid Build Coastguard Worker UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); 303*90c8c64dSAndroid Build Coastguard Worker descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 304*90c8c64dSAndroid Build Coastguard Worker mBluetoothGatt.writeDescriptor(descriptor); 305*90c8c64dSAndroid Build Coastguard Worker } 306*90c8c64dSAndroid Build Coastguard Worker } 307*90c8c64dSAndroid Build Coastguard Worker 308*90c8c64dSAndroid Build Coastguard Worker /** 309*90c8c64dSAndroid Build Coastguard Worker * Retrieves a list of supported GATT services on the connected device. This should be 310*90c8c64dSAndroid Build Coastguard Worker * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully. 311*90c8c64dSAndroid Build Coastguard Worker * 312*90c8c64dSAndroid Build Coastguard Worker * @return A {@code List} of supported services. 313*90c8c64dSAndroid Build Coastguard Worker */ getSupportedGattServices()314*90c8c64dSAndroid Build Coastguard Worker public List<BluetoothGattService> getSupportedGattServices() { 315*90c8c64dSAndroid Build Coastguard Worker if (mBluetoothGatt == null) return null; 316*90c8c64dSAndroid Build Coastguard Worker 317*90c8c64dSAndroid Build Coastguard Worker return mBluetoothGatt.getServices(); 318*90c8c64dSAndroid Build Coastguard Worker } 319*90c8c64dSAndroid Build Coastguard Worker } 320