Subscribe to a BLE Gatt notification Android

Orlando picture Orlando · Nov 21, 2014 · Viewed 40.9k times · Source

I´m developing an BLE app, based on the Gatt sample project provided by google: https://developer.android.com/samples/BluetoothLeGatt/index.html. So, I can send data writing in a characteristic successfully. Now I need to know when this characteristic change its value. I understand that I need to implement the setCharacteristicNotification() method and the onDescriptorWrite() method. But I cant do that this works. Searching, I found some ideas about that: Android BLE API: GATT Notification not received, What are the steps to get notified by Bluetooth Low Energy (BLE) device?, but still no luck I dont understand how it works, what parameter I need define or what I need to do!!!. Some good soul can share me a complete project that do this. All that I need is visualize the value of the characteristic when the BLE device returns some data.

I read the official Android documentation, but it is not so specific about that. Thanks in advance

These are my codes:

Device

public class DeviceControlActivity extends Activity {

public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";

private TextView mConnectionState;
private TextView mDataField;
private TextView mRssiField;
private String mDeviceName;
private String mDeviceAddress;
private ExpandableListView mGattServicesList;
private BluetoothLeService mBluetoothLeService;
static ArrayList<ArrayList<BluetoothGattCharacteristic>> mGattCharacteristics = new      ArrayList<ArrayList<BluetoothGattCharacteristic>>();
private boolean mConnected = false;
private BluetoothGattCharacteristic mNotifyCharacteristic;
private BluetoothGattCharacteristic mWriteCharacteristic, mReadCharacteristic;
Button Escritor;
byte hello[] = { 0 };

private final String LIST_NAME = "NAME";
private final String LIST_UUID = "UUID";

//  BluetoothGattCharacteristic characteristic;

//  public BluetoothGatt mBluetoothGatt;
// Code to manage Service lifecycle.
private final ServiceConnection mServiceConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder service) {
        mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
        if (!mBluetoothLeService.initialize()) {
            Log.e("Unable to initialize Bluetooth");
            finish();
        }
        // Automatically connects to the device upon successful start-up
        // initialization.
        mBluetoothLeService.connect(mDeviceAddress);
        mBluetoothLeService.setBLEServiceCb(mDCServiceCb);
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        mBluetoothLeService = null;
    }
};

// If a given GATT characteristic is selected, check for supported features.
// This sample
// demonstrates 'Read' and 'Notify' features. See
// http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for
// the complete
// list of supported characteristic features.
private final ExpandableListView.OnChildClickListener servicesListClickListner = new ExpandableListView.OnChildClickListener() {
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    @Override
    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
        if (mGattCharacteristics != null) {
            final BluetoothGattCharacteristic characteristic = mGattCharacteristics.get(groupPosition).get(childPosition);
            final int charaProp = characteristic.getProperties();
            if ((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
                // If there is an active notification on a characteristic,
                // clear
                // it first so it doesn't update the data field on the user
                // interface.
                Log.d("BluetoothGattCharacteristic has PROPERTY_READ, so send read request");

                if (mNotifyCharacteristic != null) {
                    mBluetoothLeService.setCharacteristicNotification(mNotifyCharacteristic, true);
                    mNotifyCharacteristic = null;
                }
                mBluetoothLeService.readCharacteristic(characteristic);
                /*Toast.makeText(getApplicationContext(), "Leyó Algo",
                        Toast.LENGTH_SHORT).show();*/
            }

            if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
                Log.d("BluetoothGattCharacteristic has PROPERTY_NOTIFY, so send notify request");

                mNotifyCharacteristic = characteristic;
                mBluetoothLeService.setCharacteristicNotification(characteristic, true);
            }

            if (((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) | (charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) > 0) {
                Log.d("BluetoothGattCharacteristic has PROPERY_WRITE | PROPERTY_WRITE_NO_RESPONSE");

                mWriteCharacteristic = characteristic;
                // popup an dialog to write something.
                showCharactWriteDialog();
            }
            return true;
        }
        return false;
    }
};

private void showCharactWriteDialog() {
    DialogFragment newFrame = new BleCharacterDialogFragment();
    newFrame.show(getFragmentManager(), "blewrite");
}

private void clearUI() {
    mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);
    mDataField.setText(R.string.no_data);
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.gatt_services_characteristics);

    final Intent intent = getIntent();
    mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
    mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);

    // Sets up UI references.
    ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
    mGattServicesList = (ExpandableListView) findViewById(R.id.gatt_services_list);
    mGattServicesList.setOnChildClickListener(servicesListClickListner);
    mConnectionState = (TextView) findViewById(R.id.connection_state);
    mDataField = (TextView) findViewById(R.id.data_value);
    mRssiField = (TextView) findViewById(R.id.signal_rssi);

    getActionBar().setTitle(mDeviceName);
    getActionBar().setDisplayHomeAsUpEnabled(true);
    Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
    bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
}

@Override
protected void onResume() {
    super.onResume();
    // registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
    if (mBluetoothLeService != null) {
        final boolean result = mBluetoothLeService.connect(mDeviceAddress);
        Log.d("Connect request result=" + result);
    }
}

@Override
protected void onPause() {
    super.onPause();
    // unregisterReceiver(mGattUpdateReceiver);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    unbindService(mServiceConnection);
    mBluetoothLeService = null;
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.gatt_services, menu);
    if (mConnected) {
        menu.findItem(R.id.menu_connect).setVisible(false);
        menu.findItem(R.id.menu_disconnect).setVisible(true);
    } else {
        menu.findItem(R.id.menu_connect).setVisible(true);
        menu.findItem(R.id.menu_disconnect).setVisible(false);
    }
    return true;
}

@TargetApi(Build.VERSION_CODES.ECLAIR)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.menu_connect:
        mBluetoothLeService.connect(mDeviceAddress);
        return true;
    case R.id.menu_disconnect:
        mBluetoothLeService.disconnect();
        return true;
    case android.R.id.home:
        onBackPressed();
        return true;
    }
    return super.onOptionsItemSelected(item);
}

private void updateConnectionState(final int resourceId) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mConnectionState.setText(resourceId);
        }
    });
}

private void displayData(String data) {
    if (data != null) {
        mDataField.setText(data);

    }
}

private void displayRssi(String rssi) {
    if (rssi != null) {
        // Log.d("-- dispaly Rssi: " + rssi);
        mRssiField.setText(rssi);
    }
}

private void displayRetorno(String rssi) {

    if (rssi != null) {
        // Log.d("-- dispaly Rssi: " + rssi);
        mRssiField.setText(rssi+"Ñ");
        Toast.makeText(getApplicationContext(), "SsSsSs"+"viendo", Toast.LENGTH_SHORT).show();
    }
}

// Demonstrates how to iterate through the supported GATT
// Services/Characteristics.
// In this sample, we populate the data structure that is bound to the
// ExpandableListView
// on the UI.
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private void displayGattServices(List<BluetoothGattService> gattServices) {
    Log.d("displayGATTServices");

    if (gattServices == null)
        return;
    String uuid = null;
    String unknownServiceString = getResources().getString(R.string.unknown_service);
    String unknownCharaString = getResources().getString(R.string.unknown_characteristic);
    ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>();
    ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData = new ArrayList<ArrayList<HashMap<String, String>>>();
    mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

    // Loops through available GATT Services.
    for (BluetoothGattService gattService : gattServices) {
        HashMap<String, String> currentServiceData = new HashMap<String, String>();
        uuid = gattService.getUuid().toString();
        currentServiceData.put(LIST_NAME, SampleGattAttributes.lookup(uuid, unknownServiceString));
        currentServiceData.put(LIST_UUID, uuid);
        gattServiceData.add(currentServiceData);

        ArrayList<HashMap<String, String>> gattCharacteristicGroupData = new ArrayList<HashMap<String, String>>();
        List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
        ArrayList<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>();

        // Loops through available Characteristics.
        for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
            charas.add(gattCharacteristic);
            HashMap<String, String> currentCharaData = new HashMap<String, String>();
            uuid = gattCharacteristic.getUuid().toString();
            currentCharaData.put(LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString));
            currentCharaData.put(LIST_UUID, uuid);
            gattCharacteristicGroupData.add(currentCharaData);
        }
        mGattCharacteristics.add(charas);
        gattCharacteristicData.add(gattCharacteristicGroupData);
    }

    SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(this, gattServiceData, android.R.layout.simple_expandable_list_item_2, new String[] { LIST_NAME, LIST_UUID },
            new int[] { android.R.id.text1, android.R.id.text2 }, gattCharacteristicData, android.R.layout.simple_expandable_list_item_2, new String[] { LIST_NAME, LIST_UUID }, new int[] {
                    android.R.id.text1, android.R.id.text2 });
    mGattServicesList.setAdapter(gattServiceAdapter);


    final BluetoothGattCharacteristic characteristic = mGattCharacteristics.get(2).get(0);
    final int charaProp = characteristic.getProperties();
    mWriteCharacteristic = characteristic;
    mBluetoothLeService.setCharacteristicNotification(mWriteCharacteristic, true);

    if (mWriteCharacteristic != null) {
        byte[] value = { (byte) 0x0D };
        mWriteCharacteristic.setValue("ATZ");
        Toast.makeText(getApplicationContext(), String.valueOf(mWriteCharacteristic.getStringValue(0)), Toast.LENGTH_SHORT).show();
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue(value);
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        boolean jo=mBluetoothLeService.mBluetoothGatt.readCharacteristic(mWriteCharacteristic);
        Toast.makeText(getApplicationContext(), String.valueOf(mWriteCharacteristic.getStringValue(0)), Toast.LENGTH_SHORT).show();

        /*mWriteCharacteristic.setValue("ATE0");
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue(value);
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue("ATE0");
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue(value);
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue("ATL0");
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue(value);
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue("ATM0");
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue(value);
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue("ATS0");
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue(value);
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue("ATH0");
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue(value);
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue("ATAT2");
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue(value);
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);*/
        /*mWriteCharacteristic.setValue("ATZ");
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
        mWriteCharacteristic.setValue(value);
        mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);*/

    }

    final BluetoothGattCharacteristic characteristic2 = mGattCharacteristics.get(0).get(0);
    final int charaProp2 = characteristic2.getProperties();

    mReadCharacteristic = characteristic2;
    mBluetoothLeService.readCharacteristic(mReadCharacteristic);
}



private DCServiceCb mDCServiceCb = new DCServiceCb();

public class DCServiceCb implements BluetoothLeService.BLEServiceCallback {

    @Override
    public void displayRssi(final int rssi) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                DeviceControlActivity.this.displayRssi(String.valueOf(rssi));
            }
        });
    }

    @Override
    public void displayRetorno (final int rssi) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                DeviceControlActivity.this.displayRetorno("algo!!!!");
            }
        });
    }

    @Override
    public void displayData(final String data) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                DeviceControlActivity.this.displayData(data);
                Toast.makeText(getApplicationContext(), data+"SsSsSs", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void notifyConnectedGATT() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mConnected = true;
                updateConnectionState(R.string.connected);
                invalidateOptionsMenu();
            }
        });
    }

    @Override
    public void notifyDisconnectedGATT() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mConnected = false;
                updateConnectionState(R.string.disconnected);
                invalidateOptionsMenu();
                clearUI();
            }
        });
    }

    @Override
    public void displayGATTServices() {
        Log.d("displayGATTServices.");
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (mBluetoothLeService != null) {
                    DeviceControlActivity.this.displayGattServices(mBluetoothLeService.getSupportedGattServices());
                }
            }
        });
    }
}

@SuppressLint("ValidFragment")
public class BleCharacterDialogFragment extends DialogFragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.write_charact_dialog, container, false);
        final EditText ed = (EditText) v.findViewById(R.id.charact_value);
        Button ok = (Button) v.findViewById(R.id.dialog_confirm);
        Button cancel = (Button) v.findViewById(R.id.dialog_cancel);

        ok.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {


                if (mWriteCharacteristic != null) {
                    mWriteCharacteristic.setValue("AA");
                    mBluetoothLeService.mBluetoothGatt.writeCharacteristic(mWriteCharacteristic);
                }

                // characteristic.setValue("FF");
                // characteristic.setValue(new byte[] {(byte) 0xFF});
                // writeCharacteristicValue(characteristic);
                //
                Toast.makeText(getApplicationContext(), "Se envío el dato", Toast.LENGTH_SHORT).show();
                dismiss();
                return;
            }
        });

        cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });

        return v;
    }
}

public void writeCharacteristicValue(BluetoothGattCharacteristic characteristica) {
    byte[] value = { (byte) 0xFF };
    characteristica.setValue(bytesToHex(value));
    boolean status = mBluetoothLeService.mBluetoothGatt.writeCharacteristic(characteristica);
}

final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();

public String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for (int j = 0; j < bytes.length; j++) {
        v = bytes[j] & 0xFF;
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return new String(hexChars);
 }
}

Service

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class BluetoothLeService extends Service {
// private final static String TAG =
// BluetoothLeService.class.getSimpleName();

private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private String mBluetoothDeviceAddress;
BluetoothGatt mBluetoothGatt;
private int mConnectionState = STATE_DISCONNECTED;
private BLEServiceCallback mBLEServiceCb = null;

private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
private static final int GATT_SUCCESS=0;

public final static UUID UUID_HEART_RATE_MEASUREMENT =  UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

private final long READING_RSSI_TASK_FREQENCY = 500;

private static final int READ_RSSI_REPEAT = 1;

private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case READ_RSSI_REPEAT:
            if (mBluetoothGatt != null) {
                mBluetoothGatt.readRemoteRssi();
                if(DeviceControlActivity.mGattCharacteristics.size()==3)
                {
                    boolean pp=mBluetoothGatt.readCharacteristic(DeviceControlActivity.mGattCharacteristics.get(2).get(0));//
                    //BluetoothGattCallback.onCharacteristicRead(mBluetoothGatt, DeviceControlActivity.mGattCharacteristics.get(2).get(0), GATT_SUCCESS);
                }
            }
            sendMessageDelayed(obtainMessage(READ_RSSI_REPEAT), READING_RSSI_TASK_FREQENCY);
            break;
        }
    }
};

private void startReadRssi() {
    if (mHandler.hasMessages(READ_RSSI_REPEAT)) {
        Log.w("+++++++++ Handler already has Message: READ_RSSI_REPEAT");
    }
    mHandler.sendEmptyMessage(READ_RSSI_REPEAT);
}

private void stopReadRssi() {
    mHandler.removeMessages(READ_RSSI_REPEAT);
}

// Implements callback methods for GATT events that the app cares about. For
// example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        Log.d("onConnectionStateChange status = " + status + ", newState = " + newState);

        if (newState == BluetoothProfile.STATE_CONNECTED) {
            if (mBLEServiceCb != null) {
                mBLEServiceCb.notifyConnectedGATT();
            }

            Log.d("Connected to GATT server.");
            // Attempts to discover services after successful connection.
            Log.d("Attempting to start service discovery:" + mBluetoothGatt.discoverServices());
            startReadRssi();
            //si se conecta empiece a leer mi caracteritica

        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            if (mBLEServiceCb != null) {
                mBLEServiceCb.notifyDisconnectedGATT();
            }
            stopReadRssi();
            Log.d("Disconnected from GATT server.");
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        Log.d("onServicesDiscovered status = " + status);

        if (status == BluetoothGatt.GATT_SUCCESS) {
            if (mBLEServiceCb != null) {
                mBLEServiceCb.displayGATTServices();
            }

        } else {
            Log.d("onServicesDiscovered received: " + status);
        }
        setCharacteristicNotification(DeviceControlActivity.mGattCharacteristics.get(2).get(0), true);//puede que sea mejor no ponerlo, pero no se
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        Log.d("onCharacteristicRead status: " + status);
        Toast.makeText(getApplicationContext(), "MMMM"+"BBB"+"SsSsSs", Toast.LENGTH_SHORT).show();
        if (status == BluetoothGatt.GATT_SUCCESS) {
            displayCharacteristic(characteristic);
        }
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        Log.d("------------- onCharacteristicWrite status: " + status);

        // handler callback of write characteristic.
        // do somethings here.
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        Log.d("onCharacteristicChanged");
        Toast.makeText(getApplicationContext(), "se ejecutó el metodo de cambio en caracteristica", Toast.LENGTH_SHORT).show();
        displayCharacteristic(characteristic);
    }

    @Override
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
        // Log.d("onReadRemoteRssi rssi = " + rssi + "; status = " +
        // status);
        if (mBLEServiceCb != null) {
            mBLEServiceCb.displayRssi(rssi);
        }
    }
};

public void setBLEServiceCb(BLEServiceCallback cb) {
    if (cb != null) {
        mBLEServiceCb = cb;
    }
}

private void displayCharacteristic(final BluetoothGattCharacteristic characteristic) {
    String msg = null;
    // This is special handling for the Heart Rate Measurement profile. Data
    // parsing is
    // carried out as per profile specifications:
    // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
    if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
        int flag = characteristic.getProperties();
        int format = -1;
        if ((flag & 0x01) != 0) {
            format = BluetoothGattCharacteristic.FORMAT_UINT16;
            Log.d("Heart rate format UINT16.");
        } else {
            format = BluetoothGattCharacteristic.FORMAT_UINT8;
            Log.d("Heart rate format UINT8.");
        }
        final int heartRate = characteristic.getIntValue(format, 1);
        Log.d(String.format("Received heart rate: %d", heartRate));

        msg = String.valueOf(heartRate);
    } else {
        // For all other profiles, writes the data formatted in HEX.
        final byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(data.length);
            for (byte byteChar : data)
                stringBuilder.append(String.format("%02X ", byteChar));

            msg = new String(data) + "\n" + stringBuilder.toString();
        }
    }

    if (mBLEServiceCb != null) {
        mBLEServiceCb.displayData(msg);
    }

}

public class LocalBinder extends Binder {
    BluetoothLeService getService() {
        return BluetoothLeService.this;
    }
}

@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}

@Override
public boolean onUnbind(Intent intent) {
    close();
    return super.onUnbind(intent);
}

private final IBinder mBinder = new LocalBinder();

public boolean initialize() {
    // For API level 18 and above, get a reference to BluetoothAdapter
    // through
    // BluetoothManager.
    if (mBluetoothManager == null) {
        mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        if (mBluetoothManager == null) {
            Log.e("Unable to initialize BluetoothManager.");
            return false;
        }
    }

    mBluetoothAdapter = mBluetoothManager.getAdapter();
    if (mBluetoothAdapter == null) {
        Log.e("Unable to obtain a BluetoothAdapter.");
        return false;
    }

    return true;
}

public boolean connect(final String address) {
    if (mBluetoothAdapter == null || address == null) {
        Log.w("BluetoothAdapter not initialized or unspecified address.");
        return false;
    }

    // Previously connected device. Try to reconnect.
    if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) {
        Log.d("Trying to use an existing mBluetoothGatt for connection.");
        if (mBluetoothGatt.connect()) {
            mConnectionState = STATE_CONNECTING;
            return true;
        } else {
            return false;
        }
    }

    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    if (device == null) {
        Log.w("Device not found.  Unable to connect.");
        return false;
    }
    // We want to directly connect to the device, so we are setting the
    // autoConnect
    // parameter to false.
    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
    Log.d("Trying to create a new connection.");
    mBluetoothDeviceAddress = address;
    mConnectionState = STATE_CONNECTING;
    return true;
}

public void disconnect() {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w("BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.disconnect();
}

public void close() {
    if (mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.close();
    mBluetoothGatt = null;
}

/**
 * Request a read on a given {@code BluetoothGattCharacteristic}. The read
 * result is reported asynchronously through the
 * {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
 * callback.
 * 
 * @param characteristic
 *            The characteristic to read from.
 */
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
    Toast.makeText(getApplicationContext(), "readCharacteristic en Servicio", Toast.LENGTH_SHORT).show();
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w("BluetoothAdapter not initialized");

        return;
    }
    mBluetoothGatt.readCharacteristic(characteristic);
}

/**
 * Requst a write on a give {@code BluetoothGattCharacteristic}. The write
 * result is reported asynchronously through the
 * {@code BluetoothGattCallback#onCharacteristicWrite(andorid.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
 * callback.
 */
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w("BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.writeCharacteristic(characteristic);
}

/**
 * Enables or disables notification on a give characteristic.
 * 
 * @param characteristic
 *            Characteristic to act on.
 * @param enabled
 *            If true, enable notification. False otherwise.
 */
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w("BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.setCharacteristicNotification(characteristic, true);

    // This is specific to Heart Rate Measurement.
    if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mBluetoothGatt.writeDescriptor(descriptor);
    }
}

/**
 * Retrieves a list of supported GATT services on the connected device. This
 * should be invoked only after {@code BluetoothGatt#discoverServices()}
 * completes successfully.
 * 
 * @return A {@code List} of supported services.
 */
public List<BluetoothGattService> getSupportedGattServices() {
    if (mBluetoothGatt == null)
        return null;

    return mBluetoothGatt.getServices();
}

public interface BLEServiceCallback {
    public void displayRssi(int rssi);

    public void displayData(String data);

    public void notifyConnectedGATT();

    public void notifyDisconnectedGATT();

    public void displayGATTServices();

    public void displayRetorno(int rssi);
 }
}

Answer

doncoleman picture doncoleman · Nov 22, 2014

To receive notification in Android you need to set characteristic notification to true

gatt.setCharacteristicNotification(characteristic, true);

You also need to set the client characteristic configuration descriptor 0x2902

// 0x2902 org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
UUID uuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(uuid);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);

A better API would be for setCharacteristicNotification to set the descriptor, but unfortunately it doesn't seem to work that way.