簡體   English   中英

489次成功連接后,android藍牙連接失敗

[英]android bluetooth connection fails after 489 successful connections

不幸的是,我的android藍牙有一些問題。 對於我的測試環境,我使用的是帶有Android 4.4.2的Nexus 4。

我的PC上有一個Java應用程序,它使用bluecove作為客戶端進行SPP連接。 該程序正在尋找一個特殊的服務名稱,並與我的Android手機連接。 然后它發送72個字節到我的Android手機,並等待答案。 得到答案時,程序會睡3秒鍾而不是重新開始。

在我的Android手機上,我有一個帶有后台藍牙監聽器的應用程序,它在啟動時啟動。 此應用程序基於BluetoothChat示例演示。 當接收藍牙數據時,我檢查傳入的數據並發回答案。

一切正常。 但是在489藍牙連接之后,Android應用程序在PC-java-app正在進行時失敗並顯示以下錯誤代碼:

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樣本時我也有同樣的行為。

所以,有人提示,會發生什么?

更新:

如果藍牙狀態為3,則每次連接后BluetoothServerSocket都會關閉,BluetoothSocket會關閉。

問題似乎與您設備上的文件描述符限制有關。 沒有為這個問題的報告在這里

在創建藍牙套接字期間, 新的fd是從系統獲取的兩個新FD 您似乎沒有正確關閉以前的BT連接,因此在您達到限制之前,已使用的FD數量會穩步增加。

為了避免這種情況,你會至少要調用close()在你從收到BluetoothServerSocket listenUsingRfcommWithServiceRecord()完成操作它后調用。 您還應該檢查是否要保留連接到BT連接的其他資源,並在可能的情況下釋放它們。


這里要求的是如何強制關閉BluetoothServerSocket的ParcelFileDescriptor。 小心:它可能會破壞事情!

您必須訪問BluetoothServerSocketmSocket字段才能訪問底層的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 ,你可以省略獲得mSocket的部分,如jitain sharmas answer所示

我對我提出的問題的解決方法是:

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的方法,以及下面我使用此代碼的方法:每當socket必須關閉時:

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。 希望它能幫助你和其他人面對同樣的問題。

這是Android 4.2 - 4.4.4(和4.4w&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
}

如果您在應用程序中使用藍牙作為服務器,我遇到了與此相同的問題,除了我的問題是當我進行反射時,我在文件描述符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());
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM