title: Android BLE: onCharacteristicChanged never fires
body: <p>I'm trying to write an Android app that mimics functionality already present in an iOS app I wrote. I am interfacing with 2 different BLE devices:</p>
  
  <ol>
  <li>Blood Pressure Cuff</li>
  <li>Weight Scale</li>
  </ol>
  
  <p>On iOS, I have both devices working well and reporting data. On Android, I can't get it to work. After hours of research and testing, I think the basic issue I'm trying to solve is this:</p>
  
  <p>On iOS, I call the following code to enable the BLE device to notify my iOS device when it has data to report:</p>
  
  <pre><code>#pragma mark - CBPeripheralDelegate Protocol methods
  - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
      for (CBCharacteristic *characteristic in [service characteristics]) {
          [peripheral setNotifyValue:YES forCharacteristic:characteristic];
      }
  }
  </code></pre>
  
  <p>That's it. The notes for this method in iOS say the following:</p>
  
  <blockquote>
    <p>If the specified characteristic is configured to allow both notifications and indications, calling this method enables notifications only.</p>
  </blockquote>
  
  <p>Based on that (and the fact that <strong>it works</strong> in iOS), I'm figuring that the configuration descriptor for the characteristic for which I want notifications should be configured like this:</p>
  
  <pre><code>descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
  gatt.writeDescriptor(descriptor);
  </code></pre>
  
  <p>With that in mind, my <code>BLEDevice</code> class looks like this:</p>
  
  <pre><code>public abstract class BLEDevice {
      protected BluetoothAdapter.LeScanCallback   mLeScanCallback;
      protected BluetoothGattCallback             mBluetoothGattCallback;
      protected byte[]                            mBytes;
      protected Context                           mContext;
      protected GotReadingCallback                mGotReadingCallback;
      protected String                            mDeviceName;
      public final static UUID                    UUID_WEIGHT_SCALE_SERVICE                       
              = UUID.fromString(GattAttributes.WEIGHT_SCALE_SERVICE);
      public final static UUID                    UUID_WEIGHT_SCALE_READING_CHARACTERISTIC        
              = UUID.fromString(GattAttributes.WEIGHT_SCALE_READING_CHARACTERISTIC);
      public final static UUID                    UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC  
              = UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
      public final static UUID                    UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR      
              = UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);
  
      abstract void processReading();
  
      interface GotReadingCallback {
          void gotReading(Object reading);
      }
  
      public BLEDevice(Context context, String deviceName, GotReadingCallback gotReadingCallback) {
          mContext                    = context;
          BluetoothManager btManager  = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
          final BluetoothAdapter btAdapter  = btManager.getAdapter();
          if (btAdapter != null &amp;&amp; !btAdapter.isEnabled()) {
              Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
              mContext.startActivity(enableIntent);
          }
          mDeviceName = deviceName;
          mBluetoothGattCallback = new BluetoothGattCallback() {
              @Override
              public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
                  byte[] data = characteristic.getValue();
                  mBytes = data;
                  Log.d("BluetoothGattCallback.onCharacteristicChanged", "data: " + data.toString());
              }
              @Override
              public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
                  // this will get called when a device connects or disconnects
                  if (newState == BluetoothProfile.STATE_CONNECTED) {
                      gatt.discoverServices();
                  } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                      if (mBytes != null) {
                          processReading();
                      }
                  }
              }
              @Override
              public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
                  super.onDescriptorWrite(gatt, descriptor, status);
                  Log.d("onDescriptorWrite", "descriptor: " + descriptor.getUuid() + ". characteristic: " + descriptor.getCharacteristic().getUuid() + ". status: " + status);
              }
              @Override
              public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
                  // this will get called after the client initiates a BluetoothGatt.discoverServices() call
                  BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
                  if (service != null) {
                      BluetoothGattCharacteristic characteristic;
                      characteristic                              = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
                      if (characteristic != null) {
                          gatt.setCharacteristicNotification(characteristic, true);
                      }
                      characteristic                              = service.getCharacteristic(UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
                      if (characteristic != null) {
                          BluetoothGattDescriptor descriptor      = characteristic.getDescriptor(UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);
                          if (descriptor != null) {
                              descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                              gatt.writeDescriptor(descriptor);
                          }
                      }
                  }
              }
          };
          mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
              @Override
              public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
                  Log.d("LeScanCallback", device.toString());
                  if (device.getName().contains("{Device Name}")) {
                      BluetoothGatt bluetoothGatt = device.connectGatt(mContext, false, mBluetoothGattCallback);
                      btAdapter.stopLeScan(mLeScanCallback);
                  }
              }
          };
          btAdapter.startLeScan(mLeScanCallback);
      }
  }
  </code></pre>
  
  <blockquote>
    <p><strong><em>NOTE:</em></strong> It might be important to know that these 2 devices function in the following way:</p>
    
    <ol>
    <li>The BLE device is turned on an a measurement is initiated on the device.</li>
    <li>Once the measurement has been taken, the BLE device attempts to initiate a BLE connection.</li>
    <li>Once the BLE connection is made, the device pretty much immediately sends the data, sometimes sending a couple of data packets. (If previous data measurements haven't been successfully sent over BLE, it keeps them in memory and sends all of them, so I only really care about the final data packet.)</li>
    <li>Once the final data packet is sent, the BLE device disconnects rapidly.</li>
    <li>If the BLE device fails to send data (as is currently happening on the Android app), the BLE device disconnects pretty rapidly.</li>
    </ol>
  </blockquote>
  
  <p>In my LogCat, I see a lot of output that's exactly like I'd expect. </p>
  
  <ol>
  <li>I see a list of services like I expect, including the data service I want.</li>
  <li>I see a list of characteristics like I expect, including the data characteristic I want.</li>
  <li>I see a list of descriptors like I expect, including the "configuration" (0x2902) descriptor.</li>
  </ol>
  
  <p>The most recent failure I'm experiencing is a status of "128" being reported in <code>onCharacteristicWrite</code>. The comments to question #3 (below) seem to indicate this is a resource issue.</p>
  
  <p>I've looked at the following questions:</p>
  
  <ol>
  <li><a href="https://stackoverflow.com/questions/24366361/android-ble-oncharacteristicchanged-not-called">Android BLE onCharacteristicChanged not called</a></li>
  <li><a href="https://stackoverflow.com/questions/24008249/android-ble-read-and-write-characteristics">Android BLE, read and write characteristics</a></li>
  <li><a href="https://stackoverflow.com/questions/18450591/android-ble-4-3-ondescriptorwrite-returns-status-128-on-enabling-characteristics">Android 4.3 onDescriptorWrite returns status 128</a></li>
  </ol>
  
  <p>Here's why they don't give me what I need:</p>
  
  <ol>
  <li>This question's answer was not to <strong>read</strong> the descriptor's value. I'm not doing that, so that can't be what's getting in the way.</li>
  <li>This is basically an overview of the various methods that are available, which I think I now understand. The big key in this question/answer is not to write multiple times to different descriptors, but I'm also not doing that. I only care about the one characteristic.</li>
  <li>This question/answer seems to be related to BLE resource limitations, but I don't think this applies. I'm only connecting this one device and I'm trying to do a very, very simple data transfer. I don't think I'm hitting resource ceilings.</li>
  </ol>
  
  <p>I've tried a bunch of examples and tutorials, including Google's Android sample code. None of them seem to enable the BLE device to notify my Android device of data updates. It's obviously not the device, since the iOS version works. So, what is the iOS code doing in the background to get the notifications to work and what code on the Android side will mimic that functionality?</p>
  
  <p><hr></p>
  
  <h1>EDIT/UPDATE</h1>
  
  <p>Based on @yonran's comments, I updated my code by changing the <code>onServicesDiscovered</code> implementation to this:</p>
  
  <pre><code>@Override
  public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
      // this will get called after the client initiates a BluetoothGatt.discoverServices() call
      BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
      if (service != null) {
          BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
          if (characteristic != null) {
              if (gatt.setCharacteristicNotification(characteristic, true) == true) {
                  Log.d("gatt.setCharacteristicNotification", "SUCCESS!");
              } else {
                  Log.d("gatt.setCharacteristicNotification", "FAILURE!");
              }
              BluetoothGattDescriptor descriptor = characteristic.getDescriptors().get(0);
              if (0 != (characteristic.getProperties() &amp; BluetoothGattCharacteristic.PROPERTY_INDICATE)) {
                  // It's an indicate characteristic
                  Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is INDICATE");
                  if (descriptor != null) {
                      descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                      gatt.writeDescriptor(descriptor);
                  }
              } else {
                  // It's a notify characteristic
                  Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is NOTIFY");
                  if (descriptor != null) {
                      descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                      gatt.writeDescriptor(descriptor);
                  }
              }
          }
      }
  }
  </code></pre>
  
  <p>That <strong><em>does</em></strong> seem to have changed some things a little bit. Here's the current Logcat, following that code change:</p>
  
  <pre><code>D/BluetoothGatt﹕ setCharacteristicNotification() - uuid: &lt;UUID&gt; enable: true
  D/gatt.setCharacteristicNotification﹕ SUCCESS!
  D/onServicesDiscovered﹕ Characteristic (&lt;UUID&gt;) is INDICATE
  D/BluetoothGatt﹕ writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb
  D/BluetoothGatt﹕ onDescriptorWrite() - Device=D0:5F:B8:01:6C:9E UUID=&lt;UUID&gt;
  D/onDescriptorWrite﹕ descriptor: 00002902-0000-1000-8000-00805f9b34fb. characteristic: &lt;UUID&gt;. status: 0
  D/BluetoothGatt﹕ onClientConnectionState() - status=0 clientIf=6 device=D0:5F:B8:01:6C:9E
  </code></pre>
  
  <p>So, it would appear that I'm now setting everything up properly (since <code>setCharacteristicNotification</code> returns <code>true</code> and the <code>onDescriptorWrite</code> status is <code>0</code>). However, <code>onCharacteristicChanged</code> still never fires.</p>
  
  <hr>