KikiTheMonk
KikiTheMonk

Reputation: 1015

Disconnecting while pairing with a remote Bluetooth Low Energy (BLE) device

Currently I start the pairing procedure (well it starts automatically to be honest) when i try to do a GATT operation and it fails to successfully complete that operation. Once the pairing procedure is finished and i get bonded with the remote device i continue to do my GATT operations in which everything works just fine.

What i have noticed is that while pairing:

1) if i disconnect from the remote device without closing the GATT client then it still pairs successfully and i go into the BluetoothDevice.BOND_BONDED state and my app continues to work just fine.
2) if i disconnect and i also close the GATT client then once i go into the BluetoothDevice.BOND_BONDED state then my app crashes

Why does my app crashes when i close the GATT client of the remote device while in the pairing procedure? is that normal or am i doing something wrong?

This is my BroadcastReceiver implementation for getting the bonding state of the remote device

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();

        if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
            final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);

            if(state == BluetoothDevice.BOND_BONDING){
                DebugWrapper.debugMsg("On Bonding...", TAG);
                mIsBonding = true;
                onBonding();
            } else if(state == BluetoothDevice.BOND_BONDED){
                DebugWrapper.debugMsg("On Bonded", TAG);
                mIsBonded = true;

                    mActivity.unregisterReceiver(mReceiver);

                    /*
                     * finish what we started
                     */
                    if(mBluetoothGatt != null){

                        if(mOperationState == OperationState.READ_CHARACTERISTIC){
                            readCharacteristic(mCurrentCharacteristic);
                        } else if(mOperationState == OperationState.WRITE_CHARACTERISTIC){
                            writeCharacteristic(mCurrentCharacteristic);
                        } else if(mOperationState == OperationState.READ_DESCRIPTOR){
                            readDescriptor(mCurrentDescriptor);
                        } else if(mOperationState == OperationState.WRITE_DESCRIPTOR){
                            writeDescriptor(mCurrentDescriptor);
                        }
                        mOperationState = OperationState.NONE;
                    }

                    onBonded();

            } else if(state == BluetoothDevice.BOND_NONE){
                DebugWrapper.debugMsg("Not Bonded", TAG);
                notBonded();
            }
        }
    }
};

Update LogCat files (Links to Pastebin.com as the files where too big to be added here)

From this logCat you can see the steps i am following

App LogCat, complete

Upvotes: 3

Views: 1695

Answers (1)

Chris Stratton
Chris Stratton

Reputation: 40407

You appear to have found a bug in bluedroid, Android's bluetooth stack.

signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000008

backtrace:
#00  pc 0007c86e /system/lib/hw/bluetooth.default.so (GKI_getnext+9)
#01  pc 000bd9e3  /system/lib/hw/bluetooth.default.so (gatt_sec_check_complete+10)
#02  pc 000bdde9  /system/lib/hw/bluetooth.default.so (gatt_enc_cmpl_cback+104)

This is a native null pointer exception in gki/common/gki_buffer.c of Android's "bluedroid" BT stack.

It happens to be a confusing one, as at first glance the problematic function appears to be GKI_getnext(), but in reality, it is actually the first instruction of the immediately following function, GKI_queue_is_empty()

BOOLEAN GKI_queue_is_empty(BUFFER_Q *p_q)
{
    return ((BOOLEAN) (p_q->count == 0));
}

If we look at the actual decompilation,

<GKI_queue_is_empty>:
    ldrh    r0, [r0, #8]
    rsbs    r0, r0, #1
    it      cc
    movcc   r0, #0
    bx      lr

We see that we are trying to reference the count member of the BUFFER_Q structure pointed to by r0, however what is happening is that we were called with a NULL buffer, so we add 8 to NULL, and try to load a register from the illegal address 00000008 causing a SIGSEGV.

Going up a level, this is called from gatt_sec_check_complete() - indeed, if we look in stack/gatt/gatt_auth.c we find the first thing it does is check if queue is empty.

void gatt_sec_check_complete(BOOLEAN sec_check_ok, tGATT_CLCB   *p_clcb, UINT8 sec_act)
{
    if (GKI_queue_is_empty(&p_clcb->p_tcb->pending_enc_clcb))
        gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_NONE);

But the problem is that the queue isn't empty, rather it is null.

So either there is a bug in bluedroid where it doesn't realize that something can be NULL, or else your program is peforming an invalid series of operations which sets the bluedroid stack up for this failure.

Assigning blame however turns out to be simple, because we can look at the security model. The process which is crashing runs as user bluetooth which is a semi-privileged account, not the user id of your application. Therefore, as a matter of basic principle, nothing your unprivileged app does should be able to crash it, and we can categorize this as a bug in the platform bluetooth stack, rather than your application.

Upvotes: 3

Related Questions