简体   繁体   English

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

[英]android bluetooth connection fails after 489 successful connections

unfortunately, I have some problems with android's bluetooth. 不幸的是,我的android蓝牙有一些问题。 For my test environment I use a Nexus 4 with Android 4.4.2. 对于我的测试环境,我使用的是带有Android 4.4.2的Nexus 4。

I have a Java Application on my PC, which uses bluecove in order to make a SPP connection as client. 我的PC上有一个Java应用程序,它使用bluecove作为客户端进行SPP连接。 The programme is looking for a special service name and connects with my android phone. 该程序正在寻找一个特殊的服务名称,并与我的Android手机连接。 Afterwards it sends 72 bytes to my android phone and waits for an answer. 然后它发送72个字节到我的Android手机,并等待答案。 When getting that answer the programme sleeps for 3 seconds and than starts again. 得到答案时,程序会睡3秒钟而不是重新开始。

On my android phone I have an application with background bluetooth listener which starts at boot. 在我的Android手机上,我有一个带有后台蓝牙监听器的应用程序,它在启动时启动。 This application is based on BluetoothChat sample demo. 此应用程序基于BluetoothChat示例演示。 When receiving bluetooth data I check incoming data and send back an answer. 当接收蓝牙数据时,我检查传入的数据并发回答案。

All that is working fine. 一切正常。 But after 489 bluetooth connections the android app fails with following error snippet while PC-java-app is going on: 但是在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)

So the app has a problem with the ParcelFileDescriptor, which is suddenly null. 因此,应用程序的ParcelFileDescriptor存在问题,该问题突然变为空。 But why? 但为什么?

All the described above also happens when changing pause-time on PC-java-app, using various data sizes for transmitting and using different smartphones. 当改变PC-java-app上的暂停时间时,使用各种数据大小来传输和使用不同的智能手机时,也会发生上述所有情况。 When using reflection "listenUsingRfcommWithServiceRecord" the same happens after 505 transmissions. 当使用反射“listenUsingRfcommWithServiceRecord”时,505传输后也会发生相同的情况。 Also using wakelock changes nothing. 使用唤醒锁也没有任何改变。

By the way, I got the same behaviour when using BluetoothChat sample. 顺便说一下,使用BluetoothChat样本时我也有同样的行为。

So, has anybody a hint, what happens? 所以,有人提示,会发生什么?

Update: 更新:

BluetoothServerSocket is closed after each connection and BluetoothSocket if bluetooth state is 3. 如果蓝牙状态为3,则每次连接后BluetoothServerSocket都会关闭,BluetoothSocket会关闭。

The problem seems connected to the limit of file descriptors on your device. 问题似乎与您设备上的文件描述符限制有关。 There is a report for that issue here 没有为这个问题的报告在这里

During the creation of a Bluetooth socket a new fd is two new FDs are acquired from the system. 在创建蓝牙套接字期间, 新的fd是从系统获取的两个新FD It seems you are not closing your previous BT connections correctly so the number of used FDs steadily increases until you hit the limit. 您似乎没有正确关闭以前的BT连接,因此在您达到限制之前,已使用的FD数量会稳步增加。

To avoid this you will at least have to call close() on the BluetoothServerSocket you receive from the listenUsingRfcommWithServiceRecord() call after finishing the operations for it. 为了避免这种情况,你会至少要调用close()在你从收到BluetoothServerSocket listenUsingRfcommWithServiceRecord()完成操作它后调用。 You should also check if you are holding on to other resources connected to the BT connection and free them if possible. 您还应该检查是否要保留连接到BT连接的其他资源,并在可能的情况下释放它们。


As it was requested here is how to force the closing of the ParcelFileDescriptor of the BluetoothServerSocket. 这里要求的是如何强制关闭BluetoothServerSocket的ParcelFileDescriptor。 Beware: it may break things! 小心:它可能会破坏事情!

You will have to access the mSocket field of the BluetoothServerSocket to access the underlying BluetoothSocket . 您必须访问BluetoothServerSocketmSocket字段才能访问底层的BluetoothSocket This BluetoothSocket holds the ParcelFileDescriptor in the field mPfd. BluetoothSocket在字段mPfd中保存ParcelFileDescriptor。 And on that you can call close() . 在那你可以调用close() As both fields are not visible you will have to use Reflections : 由于两个字段都不可见,您将不得不使用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();
}

This will close the BluetoothServerSocket . 这将关闭BluetoothServerSocket If you want to close just the BluetoothSocket from the BTServerSockets accept method you can leave out the part of getting mSocket as seen in jitain sharmas answer . 如果你想从BTServerSockets接受方法中只关闭BluetoothSocket ,你可以省略获得mSocket的部分,如jitain sharmas answer所示

My fix for the question i have asked: 我对我提出的问题的解决方法是:

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.");
        }
    }

So above is the method i have written to close the filedescriptor, and the below when i have used this code: Whenever, socket has to be close: 所以上面是我编写的关闭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.");
            }
        }
    }

I have also tried with the clearLocalSocket() method i have written, but no used for my problem. 我也试过我编写的clearLocalSocket()方法,但没有用于我的问题。 So i tried to close the FileDescriptor. 所以我试图关闭FileDescriptor。 Hope it will help you and others facing the same issue. 希望它能帮助你和其他人面对同样的问题。

This is a BluetoothSocket.close() bug in Android 4.2 - 4.4.4 (and 4.4w & L Preview)... Internally, it's calling mPfd.detachFd() and then not actually releasing the underlying file descriptor. 这是Android 4.2 - 4.4.4(和4.4w&L预览版)中的BluetoothSocket.close()错误......在内部,它调用mPfd.detachFd(),然后实际上不释放底层文件描述符。 The fix is to instead call mPfd.close() and set mPfd=null, which is exactly how Android 5.0 now does it. 修复是调用mPfd.close()并设置mPfd = null,这正是Android 5.0现在所做的。

You can mostly fix this with the other solutions posted here that call mPfd.close() and then calling your platform's BluetoothSocket.close(), but I found that wasn't quite enough for me and some weird things could happen. 您可以使用此处发布的调用mPfd.close()然后调用平台的BluetoothSocket.close()的其他解决方案来解决此问题,但我发现这对我来说还不够,可能会发生一些奇怪的事情。 I went a bit further and also clean up mSocket first and set that null, then call mPfd.close() and set that null, and finally call BluetoothSocket.close() which is needed to set mSocketState. 我更进一步,先清理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
}

If you are using bluetooth as a server in you app, I came across with the same problem as this, except my issue is I am getting null on a file descriptor mPfd when I do reflection. 如果您在应用程序中使用蓝牙作为服务器,我遇到了与此相同的问题,除了我的问题是当我进行反射时,我在文件描述符mPfd上变为null。 I was able to close the file using this method, hope this can help. 我能够使用这种方法关闭文件,希望这可以帮助。

I get the file through a id/index from LoaclSocket in BluetoothSocket using ParceFileDescriptor. 我使用ParceFileDescriptor从BluetoothSocket中的LoaclSocket通过id / index获取文件。 int mfd = 0; int mfd = 0; Field socketField = null; 字段socketField = null; LocalSocket mSocket = 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