unfortunately, I have some problems with android\'s bluetooth. For my test environment I use a Nexus 4 with Android 4.4.2.
I have a Java Application on my PC, which
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. 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. int mfd = 0; Field 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());
}
}
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. It seems you are not closing your previous BT connections correctly so the number of used FDs steadily increases until you hit the limit.
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. You should also check if you are holding on to other resources connected to the BT connection and free them if possible.
As it was requested here is how to force the closing of the ParcelFileDescriptor of the BluetoothServerSocket. Beware: it may break things!
You will have to access the mSocket field of the BluetoothServerSocket to access the underlying BluetoothSocket. This BluetoothSocket holds the ParcelFileDescriptor in the field mPfd. And on that you can call close()
. As both fields are not visible you will have to use 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. 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.
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. The fix is to instead call mPfd.close() and set mPfd=null, which is exactly how Android 5.0 now does it.
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. 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.
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
}
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:
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. So i tried to close the FileDescriptor. Hope it will help you and others facing the same issue.