489次成功连接后,android蓝牙连接失败

时间:2014-02-25 17:43:29

标签: java android bluetooth spp

不幸的是,我的android蓝牙有一些问题。对于我的测试环境,我使用的是Nexus 4和Android 4.4.2。

我的PC上有一个Java应用程序,它使用bluecove作为客户端进行SPP连接。该程序正在寻找一个特殊的服务名称,并与我的Android手机连接。然后它发送72个字节到我的Android手机,并等待答案。得到答案后,程序会睡3秒钟而不是重新开始。

在我的Android手机上,我有一个带有后台蓝牙监听器的应用程序,它在启动时启动。此应用程序基于BluetoothChat示例演示。接收蓝牙数据时,我会检查传入的数据并发回答案。

一切正常。但是在489蓝牙连接之后,当app-java-app正在进行时,android应用程序失败并显示以下错误代码:

getBluetoothService() called with no BluetoothManagerCallback
Shutting down VM
threadid=1: thread exiting with uncaught exception (group=0x41b34ba8)
FATAL EXCEPTION: main
Process: de.tum.lme.diamantum:remote_blue, PID: 21567
java.lang.NullPointerException: FileDescriptor must not be null
    at android.os.ParcelFileDescriptor.<init>(ParcelFileDescriptor.java:174)
    at android.os.ParcelFileDescriptor$1.createFromParcel(ParcelFileDescriptor.java:905)
    at android.os.ParcelFileDescriptor$1.createFromParcel(ParcelFileDescriptor.java:897)
    at android.bluetooth.IBluetooth$Stub$Proxy.createSocketChannel(IBluetooth.java:1355)
    at android.bluetooth.BluetoothSocket.bindListen(BluetoothSocket.java:349)
    at android.bluetooth.BluetoothAdapter.createNewRfcommSocketAndRecord(BluetoothAdapter.java:1055)
    at android.bluetooth.BluetoothAdapter.listenUsingRfcommWithServiceRecord(BluetoothAdapter.java:976)
    at com.test.btconn.BluetoothHandling$AcceptThread.<init>(BluetoothHandling.java:449)
    at com.test.btconn.BluetoothHandling.start(BluetoothHandling.java:216)
    at com.test.btconn.BluetoothListenerService.setupBtSockets(BluetoothListenerService.java:330)
    at com.test.btconn.BluetoothListenerService.manageBtState(BluetoothListenerService.java:249)
    at com.test.btconn.BluetoothListenerService.setBtStateDisconnected(BluetoothListenerService.java:383)
    at com.test.btconn.BluetoothListenerService.access$5(BluetoothListenerService.java:378)
    at com.test.btconn.BluetoothListenerService$2.handleMessage(BluetoothListenerService.java:421)

因此,应用程序ParcelFileDescriptor存在问题,该问题突然变为空。但为什么呢?

当改变PC-java-app上的暂停时间时,使用各种数据大小来传输和使用不同的智能手机时,也会发生上述所有情况。当使用反射“listenUsingRfcommWithServiceRecord”时,505传输后也会发生相同的情况。使用唤醒锁也没有任何改变。

顺便说一下,使用BluetoothChat样本时我也有同样的行为。

所以,有人提示,会发生什么?

更新

BluetoothServerSocket在每次连接后关闭,如果蓝牙状态为3,则关闭BluetoothSocket。

4 个答案:

答案 0 :(得分:14)

问题似乎与设备上文件描述符的限制有关。有一个针对该问题的报告here

在创建蓝牙套接字期间,从系统获取新的fd two new FDs。您似乎没有正确关闭以前的BT连接,因此在您达到限制之前,已使用的FD数量会稳步增加。

为避免这种情况,您至少必须在完成操作后从close()电话上的 BluetoothServerSocket 上拨打listenUsingRfcommWithServiceRecord()。您还应该检查是否要保留连接到BT连接的其他资源,并在可能的情况下释放它们。


这里要求的是如何强制关闭BluetoothServerSocket的ParcelFileDescriptor。 注意:它可能会破坏事情!

您必须访问 BluetoothServerSocket mSocket 字段才能访问基础 BluetoothSocket 。此 BluetoothSocket 在字段mPfd中保存ParcelFileDescriptor。在那你可以打电话给close()。由于两个字段都不可见,您必须使用 Reflections

public void closePFD(BluetoothServerSocket closeMe) throws AllKindOfExceptionsThatYouHaveToHandle
{
    Field mSocketFld = closeMe.getClass().getDeclaredField("mSocket");
    mSocketFld.setAccessible(true);

    BluetoothSocket btsock = (BluetoothSocket)mSocketFld.get(closeMe);

    Field mPfdFld = btsock.getClass().getDeclaredField("mPfd");
    mPfdFld.setAccessible(true);

    ParcelFileDescriptor pfd = (ParcelFileDescriptor)mPfdFld.get(btsock);

    pfd.close();
}

这将关闭 BluetoothServerSocket 。如果你想从BTServerSockets接受方法中只关闭 BluetoothSocket ,你就可以省去jitain sharmas answer mSocket 的部分。

答案 1 :(得分:4)

我对我提出的问题的解决方法:

private synchronized void clearFileDescriptor(){
        try{
            Field field = BluetoothSocket.class.getDeclaredField("mPfd");
            field.setAccessible(true);
            ParcelFileDescriptor mPfd = (ParcelFileDescriptor)field.get(socket);
            if(null == mPfd){
                return;
            }
            mPfd.close();
        }catch(Exception e){
            Log.w(SensorTicker.TAG, "ParcelFileDescriptor could not be cleanly closed.");
        }
    }

以上是我编写的关闭filedescriptor的方法,以及下面我使用此代码的方法: 每当插座必须关闭时:

private synchronized void cleanClose() {
        if (socket != null) {
            try {
                clearFileDescriptor();
                //clearLocalSocket();
                socket.close();         
            }
            catch (IOException e) {
                Log.w(SensorTicker.TAG, "Socket could not be cleanly closed.");
            }
        }
    }

我也尝试过我编写的clearLocalSocket()方法,但没有用于我的问题。所以我试图关闭FileDescriptor。 希望它能帮助你和其他人面对同样的问题。

答案 2 :(得分:1)

这是Android 4.2 - 4.4.4(和4.4w&amp; L预览版)中的BluetoothSocket.close()错误......在内部,它调用mPfd.detachFd()然后实际上没有释放底层文件描述符。修复是调用mPfd.close()并设置mPfd = null,这正是Android 5.0现在所做的。

您可以使用此处发布的其他解决方案来解决此问题,这些解决方案调用mPfd.close()然后调用您的平台的BluetoothSocket.close(),但我发现这对我来说还不够可能会发生一些奇怪的事情。我更进一步,先清理mSocket并设置为null,然后调用mPfd.close()并设置为null,最后调用设置mSocketState所需的BluetoothSocket.close()。

public static void cleanClose(BluetoothSocket btSocket)
{
    if(btSocket == null)
        return;

    if(Build.VERSION.SDK_INT >= 17 && Build.VERSION.SDK_INT <= 20)
    {
        try { cleanCloseFix(btSocket); }
        catch (Exception e)
        {
            Log.d(sLogName, "Exception during BluetoothSocket close bug fix: " + e.toString());
        }

        //Go on to call BluetoothSocket.close() too, because our code didn't do quite everything
    }

    //Call BluetoothSocket.close()
    try { btSocket.close(); }
    catch (Exception e)
    {
        Log.d(sLogName, "Exception during BluetoothSocket close: " + e.toString());
    }

}

private static void cleanCloseFix(BluetoothSocket btSocket) throws IOException
{
    synchronized(btSocket)
    {
        Field socketField = null;
        LocalSocket mSocket = null;
        try
        {
            socketField = btSocket.getClass().getDeclaredField("mSocket");
            socketField.setAccessible(true);

            mSocket = (LocalSocket)socketField.get(btSocket);
        }
        catch(Exception e)
        {
            Log.d(sLogName, "Exception getting mSocket in cleanCloseFix(): " + e.toString());
        }

        if(mSocket != null)
        {
            mSocket.shutdownInput();
            mSocket.shutdownOutput();
            mSocket.close();

            mSocket = null;

            try { socketField.set(btSocket, mSocket); }
            catch(Exception e)
            {
                Log.d(sLogName, "Exception setting mSocket = null in cleanCloseFix(): " + e.toString());
            }
        }


        Field pfdField = null;
        ParcelFileDescriptor mPfd = null;
        try
        {
            pfdField = btSocket.getClass().getDeclaredField("mPfd");
            pfdField.setAccessible(true);

            mPfd = (ParcelFileDescriptor)pfdField.get(btSocket);
        }
        catch(Exception e)
        {
            Log.d(sLogName, "Exception getting mPfd in cleanCloseFix(): " + e.toString());
        }

        if(mPfd != null)
        {
            mPfd.close();

            mPfd = null;

            try { pfdField.set(btSocket, mPfd); }
            catch(Exception e)
            {
                Log.d(sLogName, "Exception setting mPfd = null in cleanCloseFix(): " + e.toString());
            }
        }       

    } //synchronized
}

答案 3 :(得分:0)

如果您在应用程序中使用蓝牙作为服务器,我遇到了与此相同的问题,除了我的问题是当我进行反射时我在文件描述符mPfd上变为null。我能够使用这种方法关闭文件,希望这可以帮助。

我使用ParceFileDescriptor从BluetoothSocket中的LoaclSocket通过id / index获取文件。     int mfd = 0;         字段socketField = null;         LocalSocket mSocket = null;

    try
    {
        socketField = btSocket.getClass().getDeclaredField("mSocket");
        socketField.setAccessible(true);

        mSocket = (LocalSocket)socketField.get(btSocket);
    }
    catch(Exception e)
    {
        Log ( "Exception getting mSocket in cleanCloseFix(): " + e.toString());
    }

    if(mSocket != null)
    {
        FileDescriptor fileDescriptor =
                mSocket.getFileDescriptor();

        String in = fileDescriptor.toString();

        //regular expression to get filedescriptor index id
        Pattern p = Pattern.compile("\\[(.*?)\\]");
        Matcher m = p.matcher(in);

        while(m.find()) {
            Log ( "File Descriptor " + m.group(1));
            mfd = Integer.parseInt(m.group(1));
            break;
        }

        //Shutdown the socket properly
        mSocket.shutdownInput();
        mSocket.shutdownOutput();
        mSocket.close();

        mSocket = null;

        try { socketField.set(btSocket, mSocket); }
        catch(Exception e)
        {
            Log ("Exception setting mSocket = null in cleanCloseFix(): " + e.toString());
        }

        //Close the file descriptor when we have it from the Local Socket
        try {
            ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.adoptFd(mfd);
            if (parcelFileDescriptor != null) {
                parcelFileDescriptor.close();
                Log ( "File descriptor close succeed : FD = " + mfd);
            }
        } catch (Exception ex) {
            Log ( "File descriptor close exception " + ex.getMessage());
        }
    }