title: Android BLE can't receive Gatt Characteristic notification from device
body: <p>I'm trying to receive a notification from the device when I write a value on a characteristic, but I don´t receive anything. I enable the notification on the characteristic and then I write the value. I've seen that the characteristic in the device have changed its value but I can't get the notification. Here is my code:</p>
  
  <p>DeviceActivity:</p>
  
  <pre><code>public class DevicesActivity extends Activity {
  
  private BLEService mBluetoothLeService;
  private String mDeviceAddress;
  private boolean mConnected = false;
  private BluetoothGattCharacteristic mNotifyCharacteristic;
  
  private final ServiceConnection mServiceConnection = new ServiceConnection() {
  
      @Override
      public void onServiceConnected(ComponentName componentName, IBinder service) {
          mBluetoothLeService = ((BLEService.LocalBinder) service).getService();
          if (!mBluetoothLeService.initialize()) {
              finish();
          }
          mBluetoothLeService.context = DevicesActivity.this;
          mBluetoothLeService.connect(mDeviceAddress);
      }
  
      @Override
      public void onServiceDisconnected(ComponentName componentName) {
          mBluetoothLeService = null;
      }
  };
  
  private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
          final String action = intent.getAction();
          if (BLEService.ACTION_GATT_CONNECTED.equals(action)) {
              mConnected = true;
              invalidateOptionsMenu();
          } else if (BLEService.ACTION_GATT_DISCONNECTED.equals(action)) {
              mConnected = false;
              invalidateOptionsMenu();
          } else if (BLEService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
              List&lt;BluetoothGattService&gt; servicesList;
              servicesList = mBluetoothLeService.getSupportedGattServices();
              Iterator&lt;BluetoothGattService&gt; iter = servicesList.iterator();
              while (iter.hasNext()) {
                  BluetoothGattService bService = (BluetoothGattService) iter.next();
                  if (bService.getUuid().toString().equals(BLEUUID.SERVICE)){
                      mService = bService;
                  }
              }
          } else if (BLEService.ACTION_DATA_AVAILABLE.equals(action)) {
              displayData(intent.getStringExtra(BLEService.EXTRA_DATA));
          }
      }
  };
  
  private static final String TAG = "BLEDevice";
  public static final String EXTRA_BLUETOOTH_DEVICE = "BT_DEVICE";
  private BluetoothAdapter mBTAdapter;
  private BluetoothDevice mDevice;
  private BluetoothGatt mConnGatt;
  private int mStatus;
  BluetoothGattService mService;
  
  private EditText pinTxt;
  private Button cancelBtn;
  private Button unlockBtn;
  private Button changePinBtn;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
      setContentView(R.layout.activity_devices);
  
      pinTxt = (EditText) findViewById(R.id.pin_txt);
      cancelBtn = (Button) findViewById(R.id.cancel_btn);
      unlockBtn = (Button) findViewById(R.id.unlock_btn);
      changePinBtn = (Button) findViewById(R.id.change_pin_btn);
  
      unlockBtn.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              String aux = pinTxt.getText().toString();
              mBluetoothLeService.sendCharacteristic(aux, getBTDeviceExtra());
          }
      });
  
      mDevice = getBTDeviceExtra();
      mDeviceAddress = mDevice.getAddress();
      if (mServiceConnection == null){
          Log.v("NULL", "mServiceConnection NULL");
      }
  
      Intent gattServiceIntent = new Intent(this, BLEService.class);
      if (gattServiceIntent==null){
          Log.v("NULL", "mServiceConnection NULL");
      }
      bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
      mStatus = BluetoothProfile.STATE_DISCONNECTED;
  }
  
  private void displayData(String data) {
      if (data != null) {
          Toast.makeText(DevicesActivity.this, data, Toast.LENGTH_LONG);
      }
  }
  @Override
  protected void onDestroy() {
      super.onDestroy();
      unbindService(mServiceConnection);
      mBluetoothLeService = null;
  }
  
  @Override
  protected void onPause() {
      super.onPause();
      unregisterReceiver(mGattUpdateReceiver);
  }
  
  private static IntentFilter makeGattUpdateIntentFilter() {
      final IntentFilter intentFilter = new IntentFilter();
      intentFilter.addAction(BLEService.ACTION_GATT_CONNECTED);
      intentFilter.addAction(BLEService.ACTION_GATT_DISCONNECTED);
      intentFilter.addAction(BLEService.ACTION_GATT_SERVICES_DISCOVERED);
      intentFilter.addAction(BLEService.ACTION_DATA_AVAILABLE);
      return intentFilter;
  }
  
  @Override
  protected void onResume() {
      super.onResume();
      registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
      if (mBluetoothLeService != null) {
          final boolean result = mBluetoothLeService.connect(mDeviceAddress);
      }
  
  }
  
  private BluetoothDevice getBTDeviceExtra() {
      Intent intent = getIntent();
      if (intent
          == null) {            return null;
      }
  
      Bundle extras = intent.getExtras();
      if (extras == null) {
          return null;
      }
  
      BluetoothDevice aux = extras.getParcelable(EXTRA_BLUETOOTH_DEVICE);
      return aux;
  }
  </code></pre>
  
  <p>BLEService:</p>
  
  <pre><code> private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
  
      @Override
      public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
          String intentAction;
          if (newState == BluetoothProfile.STATE_CONNECTED) {
              intentAction = ACTION_GATT_CONNECTED;
              mConnectionState = STATE_CONNECTED;
              broadcastUpdate(intentAction);
              Log.i(TAG, "Connected to GATT server.");
              // Attempts to discover services after successful connection.
              Log.i(TAG, "Attempting to start service discovery:" +
                      mBluetoothGatt.discoverServices());
  
          } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
              intentAction = ACTION_GATT_DISCONNECTED;
              mConnectionState = STATE_DISCONNECTED;
              Log.i(TAG, "Disconnected from GATT server.");
              broadcastUpdate(intentAction);
          }
      }
  
      @Override
      public void onServicesDiscovered(BluetoothGatt gatt, int status) {
          if (status == BluetoothGatt.GATT_SUCCESS) {
              for (BluetoothGattService service : gatt.getServices()) {
                  if ((service == null) || (service.getUuid() == null)) {
                      continue;
                  }
                  if (BLEUUID.SERVICE.equalsIgnoreCase(service.getUuid().toString())) {
                      mService = service;
                  }
              }
              broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
          } else {
              Log.w(TAG, "onServicesDiscovered received: " + status);
          }
      }
  
      @Override
      public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) {
          if (status == BluetoothGatt.GATT_SUCCESS) {
              broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
              broadcastUpdate(EXTRA_DATA, characteristic);
              Log.i("CARAC","CARACTERISTICA LEIDA onCharacteristicRead()");
          }
      }
  
      @Override
      public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {
          readCharacteristic(characteristic);
          broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
          Log.i("CARAC","CAMBIO EN CARACTERISTICA");
      }
  };
  
  private void broadcastUpdate(final String action) {
      final Intent intent = new Intent(action);
      sendBroadcast(intent);
  }
  
  @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
  private void broadcastUpdate(final String action,final BluetoothGattCharacteristic characteristic) {
      final Intent intent = new Intent(action);
  
      if (PIN_CHARACTERISTIC.equals(characteristic.getUuid())) {
  
          final String pin = characteristic.getStringValue(0);
          intent.putExtra(EXTRA_DATA, String.valueOf(pin));
      } 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));
              Log.i("RECIBIDO", "RECIBIDOS DATOS");
              intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
          }
      }
      sendBroadcast(intent);
  }
  
  public class LocalBinder extends Binder {
      BLEService getService() {
          return BLEService.this;
      }
  }
  
  @Override
  public IBinder onBind(Intent intent) {
      return mBinder;
  }
  
  @Override
  public boolean onUnbind(Intent intent) {
      // After using a given device, you should make sure that BluetoothGatt.close() is called
      // such that resources are cleaned up properly.  In this particular example, close() is
      // invoked when the UI is disconnected from the Service.
      close();
      return super.onUnbind(intent);
  }
  
  private final IBinder mBinder = new LocalBinder();
  
  public boolean initialize() {
      if (mBluetoothManager == null) {
          mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
          if (mBluetoothManager == null) {
              return false;
          }
      }
      mBluetoothAdapter = mBluetoothManager.getAdapter();
      if (mBluetoothAdapter == null) {
          return false;
      }
      return true;
  }
  
  public boolean connect(final String address) {
      if (mBluetoothAdapter == null || address == null) {
          return false;
      }
  
      // Previously connected device.  Try to reconnect.
      if (mBluetoothDeviceAddress != null &amp;&amp; address.equals(mBluetoothDeviceAddress)
              &amp;&amp; mBluetoothGatt != null) {
          if (mBluetoothGatt.connect()) {
              mConnectionState = STATE_CONNECTING;
              return true;
          } else {
              return false;
          }
      }
  
      final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
      if (device == null) {
          return false;
      }
  
      mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
      mBluetoothDeviceAddress = address;
      mConnectionState = STATE_CONNECTING;
      return true;
  }
  
  public void disconnect() {
      if (mBluetoothAdapter == null || mBluetoothGatt == null) {
          return;
      }
      mBluetoothGatt.disconnect();
  }
  
  public void close() {
      if (mBluetoothGatt == null) {
          return;
      }
      mBluetoothGatt.close();
      mBluetoothGatt = null;
      Log.e("CIERRE", "CONEXION CERRADA");
  }
  
  public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
      if (mBluetoothAdapter == null || mBluetoothGatt == null) {
          return;
      }
      mBluetoothGatt.readCharacteristic(characteristic);
      Log.i("READ", "CARACTERISTICA LEIDA");
  }
  
  public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
      if (mBluetoothAdapter == null || mBluetoothGatt == null) {
          return;
      }
      mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
      if (PIN_CHARACTERISTIC.equals(characteristic.getUuid())){
          BluetoothGattDescriptor descriptor =
                  new BluetoothGattDescriptor(UUID.nameUUIDFromBytes(BLEUUID.PIN_CHARACTERISTIC_CONFIG_DESCRIPTOR.getBytes()),
                          BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED);
  
          descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
          mBluetoothGatt.writeDescriptor(descriptor);
      }
  }
  
  public List&lt;BluetoothGattService&gt; getSupportedGattServices() {
      if (mBluetoothGatt == null) return null;
      return mBluetoothGatt.getServices();
  }
  
  public void sendCharacteristic(String pin, BluetoothDevice device){
  
      byte[] pinByte = pin.getBytes();
      int pinInt = Integer.valueOf(pin);
  
      BluetoothGattCharacteristic ch = (BluetoothGattCharacteristic) mService.getCharacteristic(UUID
              .fromString(BLEUUID.PIN_CHARACTERISTIC_UUID));
  
      ch.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
  
      ch.setValue(pin);
  
      Toast.makeText(context, "CARACTERISTICA ASIGNADA", Toast.LENGTH_SHORT).show();
      connect(device.getAddress());
      mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
      setCharacteristicNotification(ch, true);
  
      if (mBluetoothGatt.writeCharacteristic(ch)) {
          Toast.makeText(context, "CARACTERISTICA ESCRITA", Toast.LENGTH_SHORT).show();
      }
  
  
  }
  </code></pre>
  
  <p>}</p>
  
  <p>BLEUUID</p>
  
  <pre><code>public class BLEUUID {
  
  // CARACTERISTICA PIN
  public  static final String SERVICE ="0000fff0-0000-1000-8000-00805f9b34fb";
  public static final String PIN_CHARACTERISTIC_UUID="0000fff9-0000-1000-8000-00805f9b34fb";
  public static final String PIN_CHARACTERISTIC_CONFIG_DESCRIPTOR="0x2902";
  
  private static HashMap&lt;String, String&gt; attributes = new HashMap();
  static {
      attributes.put(SERVICE, "Service");
      attributes.put(PIN_CHARACTERISTIC_UUID, "Pin");
  }
  </code></pre>
  
  <p>When I debug, onCharacteristicChange() never executes.</p>
  
  <p>Somebody knows where's the problem</p>