title: Subscribe to a BLE Gatt notification Android
body: <p>I´m developing an BLE app, based on the Gatt sample project provided by google: <a href="https://developer.android.com/samples/BluetoothLeGatt/index.html" rel="noreferrer">https://developer.android.com/samples/BluetoothLeGatt/index.html</a>.
  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: <a href="https://stackoverflow.com/questions/17910322/android-ble-api-gatt-notification-not-received">Android BLE API: GATT Notification not received</a>, <a href="https://stackoverflow.com/questions/20907411/what-are-the-steps-to-get-notified-by-bluetooth-low-energy-ble-device">What are the steps to get notified by Bluetooth Low Energy (BLE) device?</a>, 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.</p>
  
  <p>I read the official Android documentation, but it is not so specific about that. Thanks in advance</p>
  
  <p>These are my codes:</p>
  
  <p>Device</p>
  
  <pre><code>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&lt;ArrayList&lt;BluetoothGattCharacteristic&gt;&gt; mGattCharacteristics = new      ArrayList&lt;ArrayList&lt;BluetoothGattCharacteristic&gt;&gt;();
  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 &amp; BluetoothGattCharacteristic.PROPERTY_READ) &gt; 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 &amp; BluetoothGattCharacteristic.PROPERTY_NOTIFY) &gt; 0) {
                  Log.d("BluetoothGattCharacteristic has PROPERTY_NOTIFY, so send notify request");
  
                  mNotifyCharacteristic = characteristic;
                  mBluetoothLeService.setCharacteristicNotification(characteristic, true);
              }
  
              if (((charaProp &amp; BluetoothGattCharacteristic.PROPERTY_WRITE) | (charaProp &amp; BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) &gt; 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&lt;BluetoothGattService&gt; 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&lt;HashMap&lt;String, String&gt;&gt; gattServiceData = new ArrayList&lt;HashMap&lt;String, String&gt;&gt;();
      ArrayList&lt;ArrayList&lt;HashMap&lt;String, String&gt;&gt;&gt; gattCharacteristicData = new ArrayList&lt;ArrayList&lt;HashMap&lt;String, String&gt;&gt;&gt;();
      mGattCharacteristics = new ArrayList&lt;ArrayList&lt;BluetoothGattCharacteristic&gt;&gt;();
  
      // Loops through available GATT Services.
      for (BluetoothGattService gattService : gattServices) {
          HashMap&lt;String, String&gt; currentServiceData = new HashMap&lt;String, String&gt;();
          uuid = gattService.getUuid().toString();
          currentServiceData.put(LIST_NAME, SampleGattAttributes.lookup(uuid, unknownServiceString));
          currentServiceData.put(LIST_UUID, uuid);
          gattServiceData.add(currentServiceData);
  
          ArrayList&lt;HashMap&lt;String, String&gt;&gt; gattCharacteristicGroupData = new ArrayList&lt;HashMap&lt;String, String&gt;&gt;();
          List&lt;BluetoothGattCharacteristic&gt; gattCharacteristics = gattService.getCharacteristics();
          ArrayList&lt;BluetoothGattCharacteristic&gt; charas = new ArrayList&lt;BluetoothGattCharacteristic&gt;();
  
          // Loops through available Characteristics.
          for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
              charas.add(gattCharacteristic);
              HashMap&lt;String, String&gt; currentCharaData = new HashMap&lt;String, String&gt;();
              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 &lt; bytes.length; j++) {
          v = bytes[j] &amp; 0xFF;
          hexChars[j * 2] = hexArray[v &gt;&gt;&gt; 4];
          hexChars[j * 2 + 1] = hexArray[v &amp; 0x0F];
      }
      return new String(hexChars);
   }
  }
  </code></pre>
  
  <p>Service</p>
  
  <pre><code>@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 &amp; 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 &amp;&amp; data.length &gt; 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 &amp;&amp; address.equals(mBluetoothDeviceAddress) &amp;&amp; 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&lt;BluetoothGattService&gt; 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);
   }
  }
  </code></pre>