4

I'm attempting to connect to a paired bluetooth device every 25 seconds, scheduled through AlarmManager which triggers a WakefulBroadcastReceiver to start a service to do the connection. Once the device goes to sleep, everything works great for the first few hours, but it starts to fail after about 4-5 hours, when I assume the device goes into a deep sleep.

I get a NullPointerException from ParcelFileDescriptor, stating the "FileDescriptor must not be null". I've tried searching this error, and have even gone through the code in ParcelFileDescriptor.java, but am at a dead end. I'm running this on a Nexus 10, with Android 4.4.2. The code which tries to connect is below:

public GatewaySocket getSocket() throws IOException
{
    if (!BluetoothAdapter.checkBluetoothAddress(macAddress))
        return new GatewaySocket("Address " + macAddress + " is not a valid Bluetooth MAC Address");

    BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
    if (bluetooth == null)
        return new GatewaySocket("Sorry, no Bluetooth adapter available");

    BluetoothDevice device = bluetooth.getRemoteDevice(macAddress);

    BluetoothSocket btSocket = null;

    try
    {
        btSocket = device.createRfcommSocketToServiceRecord(uuid);
    }
    catch (Exception e)
    {
        log(3, "" + this, "Error closing socket on connection: " + e);
    }

    if (btSocket == null)
        return new GatewaySocket("Unable to launch insecure connection to " + device);

    try
    {
        btSocket.connect();
    }
    catch (IOException ex)
    {
        try
        {
                btSocket.close();
        }
        catch (IOException ex2)
        {
                // do nothing
        }

    throw (ex);
    }

    GatewaySocket socket = new GatewaySocket(btSocket, btSocket.getInputStream(), btSocket.getOutputStream());

    return socket;
}

GatewaySocket is a thin subclass of BluetoothSocket. The error occurs at the btSocket.connect() line, with the following stack trace:

01-10 09:13:57.796: W/BluetoothAdapter(3591): getBluetoothService() called with no BluetoothManagerCallback
01-10 09:13:57.801: D/BTIF_SOCK(979): service_uuid: 00001101-0000-1000-8000-00805f9b34fb
01-10 09:13:57.801: E/bt-btif(979): SOCK_THREAD_FD_RD signaled when rfc is not connected, slot id:4374, channel:-1
01-10 09:13:57.801: W/System.err(3591): java.lang.NullPointerException: FileDescriptor must not be null
01-10 09:13:57.806: W/System.err(3591):     at android.os.ParcelFileDescriptor.<init>(ParcelFileDescriptor.java:174)
01-10 09:13:57.806: W/System.err(3591):     at android.os.ParcelFileDescriptor$1.createFromParcel(ParcelFileDescriptor.java:905)
01-10 09:13:57.806: W/System.err(3591):     at android.os.ParcelFileDescriptor$1.createFromParcel(ParcelFileDescriptor.java:897)
01-10 09:13:57.806: W/System.err(3591):     at android.bluetooth.IBluetooth$Stub$Proxy.connectSocket(IBluetooth.java:1322)
01-10 09:13:57.806: W/System.err(3591):     at android.bluetooth.BluetoothSocket.connect(BluetoothSocket.java:308)
01-10 09:13:57.806: W/System.err(3591):     at com.gateway.service.AndroidGMConversation.getSocket(AndroidGMConversation.java:162)
01-10 09:13:57.806: W/System.err(3591):     at com.gateway.GatewayManagerConversation.converse(GatewayManagerConversation.java:81)
01-10 09:13:57.806: W/System.err(3591):     at com.gateway.GatewayManagerConversation.run(GatewayManagerConversation.java:72)
01-10 09:13:57.806: W/System.err(3591):     at java.lang.Thread.run(Thread.java:841)
01-10 09:13:57.806: V/PS(3591): Finished with device 06:92:25:0A:A5:50

Any suggestions would be appreciated!

physphil
  • 233
  • 2
  • 9

2 Answers2

3

I have found the workaround after going through the Bluetooth library classes, and found it doesn't close the FileDescriptor while calling bluetoothsocket.close(); despite it calls the dispatch() method over the FileDescriptor, which doesn't close it.

Just call this method before the bluetoothsocket.close();

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, "LocalSocket could not be cleanly closed.");
        }
    }

Hope this will resolve others problem as well. But it's should be handled by the BluetoothSocket class over the close method.

jitain sharma
  • 584
  • 5
  • 19
1

This looks like the BluetoothSocket.close() bug in Android 4.2 - 4.4.4, which seems to be fixed in 5.0. Basically, it's not releasing the underlying file descriptor, so it leaks until you hit your device's limit, then weird bad things happen. You need to get the ParcelFileDescriptor from the BluetoothSocket and close it yourself.

Check out my answer here for the workaround code: https://stackoverflow.com/a/27873675/1893015

Community
  • 1
  • 1
Wookie
  • 782
  • 8
  • 12