与远程蓝牙低功耗(BLE)设备配对时断开连接

3

目前当我尝试执行GATT操作并未成功完成该操作时,会自动开始配对过程(实际上是自动启动)。一旦配对过程完成并且我与远程设备建立了绑定关系,我就可以继续进行GATT操作,一切都正常。

我注意到在配对过程中:

1)如果我断开与远程设备的连接却没有关闭GATT客户端,那么它仍然能够成功配对,并且我进入BluetoothDevice.BOND_BONDED状态,我的应用程序继续正常工作。
2)如果我断开连接并且关闭了GATT客户端,则一旦进入BluetoothDevice.BOND_BONDED状态后,我的应用程序就会崩溃。

为什么在配对过程中关闭远程设备的GATT客户端会导致应用程序崩溃?这是正常现象还是我做错了什么?

这是获取远程设备绑定状态的广播接收器实现:

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();
            }
        }
    }
};

更新 LogCat 文件(链接到 Pastebin.com,因为文件太大无法在此处添加)

从这个 LogCat 中可以看到我正在遵循的步骤

应用程序 LogCat,完整版


1
与任何Android崩溃一样,我们需要从logcat中查看堆栈跟踪,通过“Caused by”行并查看所提到的行周围的源代码,并清楚地标识出特定的行。 - Chris Stratton
我添加了BroadcastReceiver的实现和LogCat数据,但是在logcat中似乎没有任何“Caused by”行,这有点让我困惑。 - KikiTheMonk
我已经更新了我的 logcat,将其过滤为仅显示错误消息,并且实际上并没有显示太多。此外,我的应用程序似乎在 logcat 中没有显示特定的错误。如果我复制粘贴了错误的 logcat,请告诉我。 - KikiTheMonk
我稍后会发布更好的 Logcat。 - KikiTheMonk
已将完整的Logcat文件链接添加到Pastebin。 - KikiTheMonk
显示剩余2条评论
1个回答

3

您似乎在Android的蓝牙堆栈bluedroid中发现了一个错误。

信号11(SIGSEGV),代码1(SEGV_MAPERR),故障地址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)

这是Android的“bluedroid”蓝牙堆栈中gki/common/gki_buffer.c的本地空指针异常。
它恰巧是一个令人困惑的异常,因为乍一看,有问题的函数似乎是GKI_getnext(),但实际上,问题出在紧随其后的函数GKI_queue_is_empty()的第一条指令。
BOOLEAN GKI_queue_is_empty(BUFFER_Q *p_q)
{
    return ((BOOLEAN) (p_q->count == 0));
}

如果我们看实际的反编译结果,
<GKI_queue_is_empty>:
    ldrh    r0, [r0, #8]
    rsbs    r0, r0, #1
    it      cc
    movcc   r0, #0
    bx      lr

我们可以看到,我们试图引用r0指向的结构体的成员,但是实际上我们被传入了一个NULL缓冲区,所以我们将8加到NULL上,并尝试从非法地址<00000008>加载寄存器,导致SIGSEGV错误。
回到更高层次,这是从调用的 - 实际上,如果我们查看stack/gatt/gatt_auth.c,我们会发现它做的第一件事就是检查队列是否为空。
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);

但问题在于队列不是空的,而是它为空
因此,要么bluedroid存在错误,无法意识到某些内容可能为空,要么您的程序执行了一系列无效操作,导致bluedroid堆栈出现故障。
然而,归咎于谁却很简单,因为我们可以查看安全模型。崩溃的进程以用户蓝牙运行,这是一个半特权帐户,而不是您的应用程序的用户ID。因此,作为基本原则,您的非特权应用程序不应该能够使其崩溃,我们可以将其归类为平台蓝牙堆栈中的错误,而不是您的应用程序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接