BLE Android - onConnectionStateChange not being called
<p>I have a problem trying to connect to a peripheral. Sometimes the callback <code>onConnectionStateChange(...)</code> is not called after <code>BluetoothDevice#connectGatt(...)</code>. What I'm trying to achieve is fast and short connections triggered by user action.</p>

<p>This situation occurs about 1 every 10 times without specific prior action. It lasts about 20 to 30 seconds or until the application is killed and reopened. The normal sequence of steps I follow is:</p>

<ol>
<li>Scan devices to find the peripheral.</li>
<li>Call <code>BluetoothDevice#connectGatt(...)</code>. If it takes longer than 1 second to connect, it means that the connection is "stuck" and therefore it won't connect, so <code>BluetoothDevice#connectGatt(...)</code> is called again. This is done with a limit of 5 attempts.</li>
<li><code>onConnectionStateChange(...)</code> is called with <code>newState</code> CONNECTED and begins the services discovery.</li>
<li>The rest of the operations are performed without problems.</li>
<li>After disconnection <code>BluetoothGatt#close()</code> is called.</li>
</ol>

<p>The problem occurs at point 3. Sometimes <code>onConnectionStateChange(...)</code>is not called. I have noticed that most of the times the problem starts with a specific behavior. After calling <code>BluetoothDevice#connectGatt(...)</code>, <code>onConnectionStateChange(...)</code> is called with <code>newState</code> CONNECTED, but almost immediately afterwards (~40 milliseconds) is called again with <code>newStatus</code> DISCONNECTED. Due to the short time of the status change, I can deduce that the device does not even tried to make the connection and changed the state to DISCONNECTED.
The problem ends when:</p>

<ol>
<li>20-30 seconds have passed. During this time <code>onConnectionStateChange(...)</code> is never called. When the problem ends, <code>onConnectionStateChange(...)</code> is called the number of times that the app tried to connect. For example, if <code>BluetoothDevice#connectGatt(...)</code> is called 15 times, <code>onConnectionStateChange(...)</code> is called 15 times with <code>newState</code> equal to DISCONNECTED. This is curious because never in any of those connection attempts the status changed to CONNECTED. </li>
<li>The app is killed and started again.</li>
</ol>

<p>This error occurs in SDK18 and SDK 21.</p>



<pre><code>@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
    String deviceName = device.getName();
    if (deviceName == null) return;
    Log.d("BLUETOOTH CONNECTION", "Device found: " + device.getName());
    if (mMode == SCAN_MODE) {
        mListener.deviceFound(device, rssi, scanRecord);
    }
    else {
        mDevices.put(device.hashCode(), device);
        stopScan();
        // Samsung devices with SDK 18 or 19 requires that connectGatt is called in main thread.
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.d("BLUETOOTH CONNECTION", "Executing first device.connectGatt()");
                BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback);
                retryIfNecessary(device, gatt);
                mTryingToConnect = true;
            }
        });
    }
}
</code></pre>



<pre><code>private void retryIfNecessary(final BluetoothDevice device, final BluetoothGatt gatt) {
    if (isRetryLimitReached()) {
        Log.d("BLUETOOTH CONNECTION", "Try count limit reached");
        finishConnection(gatt);
        mRetryCount = 0;
        mListener.error(TIMEOUT);
        return;
    }
    mRetryCount++;
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            Log.d("BLUETOOTH CONNECTION", "Check if it is frozen.");
            if (isWorking()) {
                Log.d("BLUETOOTH CONNECTION", "Frozen, create new connection.");
                BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback);
                retryIfNecessary(device, gatt);
            }
        }
    }, RETRY_INTERVAL_MS);
}
</code></pre>



<pre><code>    @Override
    public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) {
        Log.d("BLUETOOTH CONNECTION", "On connection state changed. Device: "+ gatt.getDevice().getAddress());
        if (!mConnected &amp;&amp; BluetoothGatt.STATE_CONNECTED == newState) {
            Log.d("BLUETOOTH CONNECTION", "Connected");
            mTryingToConnect = false;
            mTryingToDiscoverServices = true;
            mConnected = true;
            gatt.discoverServices();
        }
        else if(BluetoothGatt.STATE_DISCONNECTED == newState) {
            Log.d("BLUETOOTH CONNECTION", "Disconnected and closing gatt.");
            mConnected = false;
            gatt.close();
            if (!mConnectionFinished &amp;&amp; mRetryCount == 0) {
                finishConnection(gatt);
            }
        }
    }
</code></pre>

<p>I think that the peripheral is not relevant, because the iOS app can always connect without this problem.</p>

<p>Any ideas? Thanks in advance.</p>

<p><strong>Edit!</strong></p>

<p><a href="https://stackoverflow.com/questions/22214254/android-ble-connect-slowly/23749770#23749770">This</a> answer say that:</p>

<blockquote>
  <p>Direct connection has interval of 60ms and window of 30ms so
  connections complete much faster. Additionally there can only be one
  direct connection request pending at a time and it times out after 30
  seconds. onConnectionStateChange() gets called with state=2,
  status=133 to indicate this timeout.</p>
</blockquote>

<p>So in this 30 seconds interval there is a pending connection request and times out at the second 30. It's unlikely but, is there anything I can do to make this time shorter? Or maybe there is an explanation for the connection failure that I am not seeing. Thanks.</p>

<p><strong>EDIT 02/03/2016</strong></p>

<p>A new information that may help. When the problem starts (when <code>onConnectionStateChange(...)</code> is called with <code>newState=DISCONNECTED</code> after ~40ms of being called with <code>newState=CONNECTED</code>), the status is 62 = 0x03E. Looking <a href="https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/11ade82/stack/include/gatt_api.h" rel="nofollow noreferrer">here</a> that status code means GATT_CONN_FAIL_ESTABLISH. When I detect this status I'm closing the gatt connection, but the problem persists. I also tried disconnecting and closing. Ideas? Thanks.</p>