Android ble cannot receive GATT feature notifications from the device
When I write a value on a feature, I'm trying to receive a notification from the device, but I don't receive anything. I enable notification about the feature, and then I write the value. I've seen that the feature in the device has changed its value, but I can't get notification. This is my code:
DeviceActivity:
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<BluetoothGattService> servicesList;
            servicesList = mBluetoothLeService.getSupportedGattServices();
            Iterator<BluetoothGattService> 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;
}
BLEService:
 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 && data.length > 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 && address.equals(mBluetoothDeviceAddress)
            && 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<BluetoothGattService> 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();
    }
}
}
BLEUUID
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<String, String> attributes = new HashMap();
static {
    attributes.put(SERVICE, "Service");
    attributes.put(PIN_CHARACTERISTIC_UUID, "Pin");
}
Oncharacteristicchange () never executes when I debug
Someone knows what the problem is
resolvent:
The following are the general modes required to use ble on Android:
>You try to connect > you get a callback indicating that it is connected > you discover the service > you are told that the service is discovered > you get the feature > for each feature, you can get the descriptor > for the descriptor, you set it to use bluetoothgattdescriptor. Setvalue() to enable notification / Instruction > you use bluetoothgatt. Writedescriptor() Write descriptor > you can use Bluetooth GATT. Setcharacteristicnotification() to enable feature notification locally. Without this, you will not be recalled. > you receive notification that the descriptor has been written > now you can write data to the feature. All feature and descriptor configurations are completed before writing anything to any feature
