Writing data to Android Bluetooth output stream throws: IllegalMonitorStateException - android

I have an Android Bluetooth application which manages a couple of remote devices(Capsules).
Writing data to the socket output stream of a Capsule worked yesterday, and after medium scale refactoring to the Android application only, I get the following error:
java.lang.IllegalMonitorStateException: attempt to unlock read lock, not locked by current thread.
Here is the socket creation code:
public final void connectWithCapsule(Capsule capsule)
throws Exception {
BluetoothSocket socket = capsulesSockets.get(capsule);
if (socket == null) {
try {
// Method m = capsule.getBT_Device().getClass().getMethod("createRfcommSocket", new Class[]{int.class});
// socket = (BluetoothSocket) m.invoke(capsule.getBT_Device(), Integer.valueOf(17));
socket = capsule.getBT_Device().createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
} catch (Exception e) {
logError("Error creating RFcomm socket", e);
throw e;
}
capsulesSockets.put(capsule, socket);
}
try {
socket.connect();
} catch (IOException e) {
logError("Error connecting socket", e);
try {
socket.close();
} catch (IOException e1) {
logError("Error closing socket", e1);
}
capsulesSockets.remove(capsule);
throw e;
}
}
and the model which manages the in/out streams:
public final class KitBT_ConnectionModel {
private final OutputStream[] outputStreams;
private final InputStream[] inputStreams;
public KitBT_ConnectionModel(OutputStream[] outputStreams, InputStream[] inputStreams) {
super();
this.outputStreams = outputStreams;
this.inputStreams = inputStreams;
}
public void transmitData(byte[] bs)
throws IOException {
for (OutputStream outputStream : outputStreams) {
outputStream.write(bs); // THIS LINE THROWS THE EXCEPTION
outputStream.flush();
}
}
public InputStream[] getInputStreams() {
return inputStreams;
}
}
Note: I do not perform any action with both of the streams, and the first write causes the exception.
First thing that pops to mind is which thread puts the read lock and when?
I've tried to play around with the threads which call the socket creation, and the streams transactions, I've made sure, 100% sure they have both been accessed by the same thread (and also tried accessing with different threads), but this exception persists.
Please enlighten me...

HAAAAAAAAAAAAAAAAAAa.........
Darn this LG phones!!!!
I gave the phone a hard reboot, removed the battery and started it over, and it works again...
turning the Bluetooth off and on didn't do the trick! I've been doing it for the past day or so.
God damn it nearly 24 hours of waste for nothing....
How messed up can these products be!
at least it works now!

Related

Android 9.0 creating a socket out on main thread cause android.os.NetworkOnMainThreadException

I encountered this error often lately... and it's something that started to happen only on Android 9.0.
This method just create the DatagramSocket for the connection I'm going to use (on separate AsyncTask)
private boolean initLink() {
Log.d(this.getClass().getName(), "initLink()");
try {
platformIPAddr = InetAddress.getByName(platformIPAddrStr);
socket = new DatagramSocket();
socket.setReuseAddress(true);
} catch (Throwable e) {
Log.e(this.getClass().getName(), e.getMessage());
e.printStackTrace();
return false;
}
return true;
}
If the creation of the socket fails for some error (i.e. corrupted value on platformIPAddrStr) the exception thrown by the system is:
android.os.NetworkOnMainThreadException
While I would/should expect UnknownHostException or SecurityException.
class SendCommandTask extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
sendCommands();
}
catch (Throwable e)
{
Log.e(this.getClass().getName(), e.getMessage());
}
return null;
}
}
And if you are wondering it if InitLink() failed SendCommands() will not use network resources.
This exception is quite old and will be thrown when you attempt to perform any networking on the main thread and if your targetSDK is Honeycomb or newer (SDK 11).
You should create your socket asynchronously, since the socket creation can also take some time.
See https://developer.android.com/reference/android/os/NetworkOnMainThreadException

Android TCP Socket InputStrem Intermittent Read or too Slow

I need to implement a TCP comunication between an IoT device(custom) and an Android App.
For the Wifi device we have a Server Socket, while in Android i have an AsyncTask as a Client Socket. Both the device and the smarthone are connected to the same network.
Here is the Android Client Socket code for the initialization/socket-read and socket-write:
Variables:
static public Socket nsocket; //Network Socket
static public DataInputStream nis; //Network Input Stream
static private OutputStream nos; //Network Output Stream
AsyncTask method doInBackgroud:
#Override
protected Boolean doInBackground(Void... params) { //This runs on a different thread
boolean result = false;
try {
//Init/Create Socket
SocketInit(IP, PORT);
// Socket Manager
SocketUpdate();
} catch (IOException e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: IOException");
clearCmdInStack();
MainActivity.SocketDisconnectAndNetworkTaskRestart();
result = true;
} catch (Exception e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: Exception");
result = true;
} finally {
try {
SocketDisconnect();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Log.i("AsyncTask", "doInBackground: Finished");
}
return result;
}
Socket Initializzation:
public void SocketInit(String ip, int port) throws IOException {
InetAddress addr = InetAddress.getByName(ip);
SocketAddress sockaddr = new InetSocketAddress(addr, port);
nsocket = new Socket();
nsocket.setReuseAddress(false);
nsocket.setTcpNoDelay(true);
nsocket.setKeepAlive(true);
nsocket.setSoTimeout(0);
nsocket.connect(sockaddr, 0);
StartInputStream();
StartOutputStream();
}
Read from Socket:
private void SocketUpdate() throws IOException, ClassNotFoundException {
int read = 0;
// If connected Start read
if (socketSingleton.isSocketConnected()) {
// Print "Connected!" to UI
setPublishType(Publish.CONNECTED);
publishProgress();
if(mConnectingProgressDialog != null)
mConnectingProgressDialog.dismiss(); //End Connecting Progress Dialog Bar
//Set Communications Up
setCommunicationsUp(true);
Log.i("AsyncTask", "doInBackground: Socket created, streams assigned");
Log.i("AsyncTask", "doInBackground: Waiting for inital data...");
byte[] buffer = new byte[3];
do{
nis.readFully(buffer, 0, 3);
setPublishType(Publish.READ);
publishProgress(buffer);
}while(!isCancelled());
SocketDisconnect();
}
}
Streams init:
public void StartInputStream() throws IOException{
nis = new DataInputStream(nsocket.getInputStream());
}
public void StartOutputStream() throws IOException{
nos = nsocket.getOutputStream();
}
Read and Write methods:
public int Read(byte[] b, int off, int len) throws IOException{
return nis.read(b, off, len); //This is blocking
}
public void Write(byte b[]) throws IOException {
nos.write(b);
nos.flush();
}
public boolean sendDataToNetwork(final String cmd)
{
if (isSocketConnected())
{
Log.i("AsyncTask", "SendDataToNetwork: Writing message to socket");
new Thread(new Runnable()
{
public void run()
{
try
{
Write(cmd.getBytes());
}
catch (Exception e)
{
e.printStackTrace();
Log.i("AsyncTask", "SendDataToNetwork: Message send failed. Caught an exception");
}
}
}).start();
return true;
}
Log.i("AsyncTask", "SendDataToNetwork: Cannot send message. Socket is closed");
return false;
}
The application is very simple, the android app sends a command(via sendDataToNetwork method) to the IoT device and the latter sends back an "ACK" Command string.
The problem
The problem is that while the IoT device always receives the command, the smartphone rarely gets the ACK back. Sometimes i get something like "ACKACKACKACK". By debugging the IoT device i'm sure that it successfully sends back the ACK, so the problem lies in the InputStream read() method which doesn't retrieve the string right away.
Is there a way to empty the InputStream buffer right away, so that i get an "ACK" string back from the IoT device every time i send a command?
Update
I've updated the socket config so that there are no more buffer limitations and i've replaced read() method with readFully. It greatly improved, but still make some mistakes. For istance one out of 2-3 times no ack is received and i get 2 ack the next turn. Is this perhaps the computational limit of the IoT device? Or is there still margin for a better approach?
the problem lies in the InputStream read() method which doesn't empty the buffer right away.
I don't know what 'empty the buffer' means here, but InputStream.read() is specified to return as soon as even one byte has been transferred.
Is there a way to empty the InputStream buffer right away, so that i get an "ACK" string back from the IoT device every time i send a command?
The actual problem is that you could be reading more than one ACK at a time. And there are others.
If you're trying to read exactly three bytes, you should be using DataInputStream.readFully() with a byte array of three bytes.
This will also get rid of the need for the following array copy.
You should not mess with the socket buffer sizes except to increase them. 20 and 700 are both ridiculously small values, and will not be the actual values used, as the platform can adjust the value supplied. Your claim that this improved things isn't credible.
You should not spin-loop while available() is zero. This is literally a waste of time. Your comment says you are blocked in the following read call. You aren't, although you should be. You are spinning here. Remove this.

Null Pointer exception when initilizing socket

I have created one android Bluetooth program which communicates with serial port. In my program I have 3 buttons: Connect, Select & Disconnect. Connect is used for enabling Bluetooth. Select is used for retrieving data from serial port. Disconnect is for disconnecting Bluetooth and the socket which I obtained to retrieve data, and to initialize the socket as null.
btnDisConnect.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try{
mBluetoothAdapter.disable();
mmSocket.close();
mmSocket=null;
} catch(Exception e) {
Toast.makeText(getApplicationContext(), "Unable to Close.Try again", Toast.LENGTH_LONG).show();
}
}
});
My problem is when I tried to initialize the socket as null it shows Null Pointer exception.
I want to make this socket as null for further work. How can I make it null on buttonclick?
Please remember to close your Input/output streams first, then close the socket.
By closing the streams, you kick off the disconnect process. After you close the socket, the connection should be fully broken down.
If you close the socket before the streams, you may be bypassing certain shutdown steps, such as the (proper) closing of the physical layer connection.
Here's the method I use when its time to breakdown the connection.
/**
* Reset input and output streams and make sure socket is closed.
* This method will be used during shutdown() to ensure that the connection is properly closed during a shutdown.
* #return
*/
private void resetConnection() {
if (mBTInputStream != null) {
try {mBTInputStream.close();} catch (Exception e) {}
mBTInputStream = null;
}
if (mBTOutputStream != null) {
try {mBTOutputStream.close();} catch (Exception e) {}
mBTOutputStream = null;
}
if (mBTSocket != null) {
try {mBTSocket.close();} catch (Exception e) {}
mBTSocket = null;
}
}

Not returning from accept when trying to listen to bluetooth communication - Android

I'm trying to establish a bluetooth communication between an android phone/tablet (4.0.3), and a bluetooth device, which is an earring reader (Destron Fearring DTR3E, in case you want to know, which I don't suppose you do).
I paired the phone with the reader (the reader has the pairing passcode on a tag) from the bluetooth settings, bluetooth is on of course, and now I'm trying to listen to reads from the device, by means of BluetoothServerSocket. The problem is that the accept call never returns, so obviously I am doing something wrong. The communication is done using RFCOMM.
Code:
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
public AcceptThread() {
// Use a temporary object that is later assigned to mmServerSocket,
// because mmServerSocket is final
BluetoothServerSocket tmp = null;
try {
// MY_UUID is the app's UUID string, also used by the client code
String uuid = "00001101-0000-1000-8000-00805F9B34FB";
tmp = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("pdfParserServer", UUID.fromString(uuid));
} catch (Exception e) {
e.printStackTrace();
}
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned
while (true) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if (socket != null) {
// Do work to manage the connection (in a separate thread)
try {
mmServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
/** Will cancel the listening socket, and cause the thread to finish */
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) { }
}
}
Is there something I am missing?
Thank you!
The only reason that could cause the code never to come back from accept is that, the device "Destron Fearring DTR3E" you are trying to connect to, has actually a bluetoothserver socket and not a bluetooth client, hence, the device might be waiting for you to actually connect to it, in stead of you creating a bluetoothserver socket and waiting for it to connect to your android device, you should read the specs on the device and make sure that actually is you the one that has to open a connection on "Destron Fearring DTR3E" socket...
Hope this helps...
Regards!

Android 4.0+ Bluetooth connection error to an embedded device: "Permission Denied"

I have the following setup:
An Android device uses a 'Client' socket to connect to a remote embedded device, The Android application uses the following code snippet to connect to the embedded device.
On the embedded device uses MindTree BT stack, where server serial socket is prepared according to some properties in the device, which the Android application is familiar with, the connection defined on the embedded device, is not secured!!
The combination of both applications works on:
2 LG phones different models (version code < 10 uses the "Normal method")
2 HTC's different models (version code < 10 uses the "Workaround method")
Pantech Tablet (version code < 13 uses the "Workaround method")
Today, I've tried the application on Samsung S3, Motorola MB886, and a Nexus 7...
All resulted in a "Permission Denied" when calling to socket.connect()... (I have the proper permissions in the manifest, otherwise it would not work on the other devices.)
All the new devices I've tested on are version code > 4.0, so I'm wondering:
Does anyone know about any changes in the API?
Perhaps Android 4.0+ forces security?
It seem that the error occur in the Bonding state, since I can see on the embedded program logs...
Any insights?
The code:
public final synchronized int connectToDevice(int connectingMethod)
throws BluetoohConnectionException {
if (socket != null)
throw new BadImplementationException("Error socket is not null!!");
connecting = true;
logInfo("+---+ Connecting to device...");
try {
lastException = null;
lastPacket = null;
if (connectingMethod == BluetoothModule.BT_StandardConnection
|| connectingMethod == BluetoothModule.BT_ConnectionTBD)
try {
socket = fetchBT_Socket_Normal();
connectToSocket(socket);
listenForIncomingSPP_Packets();
onConnetionEstablished();
return BluetoothModule.BT_StandardConnection;
} catch (BluetoohConnectionException e) {
socket = null;
if (connectingMethod == BluetoothModule.BT_StandardConnection) {
throw e;
}
logWarning("Error creating socket!", e);
}
if (connectingMethod == BluetoothModule.BT_ReflectiveConnection
|| connectingMethod == BluetoothModule.BT_ConnectionTBD)
try {
socket = fetchBT_Socket_Reflection(1);
connectToSocket(socket);
listenForIncomingSPP_Packets();
onConnetionEstablished();
return BluetoothModule.BT_ReflectiveConnection;
} catch (BluetoohConnectionException e) {
socket = null;
if (connectingMethod == BluetoothModule.BT_ReflectiveConnection) {
throw e;
}
logWarning("Error creating socket!", e);
}
throw new BluetoohConnectionException("Error creating RFcomm socket for BT Device:" + this
+ "\n BAD connectingMethod==" + connectingMethod);
} finally {
connecting = false;
}
}
protected void onConnetionEstablished() {
logInfo("+---+ Connection established");
}
private synchronized void listenForIncomingSPP_Packets() {
if (socketListeningThread != null)
throw new BadImplementationException("Already lisening on Socket for BT Device" + this);
logInfo("+---+ Listening for incoming packets");
socketListeningThread = new Thread(socketListener, "Packet Listener - " + bluetoothDevice.getName());
socketListeningThread.start();
}
private BluetoothSocket fetchBT_Socket_Normal()
throws BluetoohConnectionException {
try {
logInfo("+---+ Fetching BT RFcomm Socket standard for UUID: " + uuid + "...");
return bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
} catch (Exception e) {
throw new BluetoohConnectionException("Error Fetching BT RFcomm Socket!", e);
}
}
private BluetoothSocket fetchBT_Socket_Reflection(int connectionIndex)
throws BluetoohConnectionException {
Method m;
try {
logInfo("+---+ Fetching BT RFcomm Socket workaround index " + connectionIndex + "...");
m = bluetoothDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
return (BluetoothSocket) m.invoke(bluetoothDevice, connectionIndex);
} catch (Exception e) {
throw new BluetoohConnectionException("Error Fetching BT RFcomm Socket!", e);
}
}
private void connectToSocket(BluetoothSocket socket)
throws BluetoohConnectionException {
try {
logInfo("+---+ Connecting to socket...");
socket.connect();
logInfo("+---+ Connected to socket");
} catch (IOException e) {
try {
socket.close();
} catch (IOException e1) {
logError("Error while closing socket", e1);
} finally {
socket = null;
}
throw new BluetoohConnectionException("Error connecting to socket with Device" + this, e);
}
}
After very long long time of investigating the matter I've found one reason for the error... on some Android devices the auto Bluetooth peering is not enabled/allowed.
So, apparently except for two connection method, there are also two Bluetooth adapter enabling method, one would be to throw an intent to ask the system to turn the adapter on, and the other is to call onto the BluetoothAdapter.enable() method, which enables the Bluetooth silently.
The first method, pops a confirmation dialog, and require user interaction while the other does not, and while not showing the Bluetooth enabling confirmation dialog, also the peering confirmation is not shown, which causes the connection error.
Using the first adapter enabling method solves the problem on most of the devices, like the Nexus 7, Samsung S3, and a few others, but on some devices there is still an issue, and I'm not really sure why, but this is much better since many devices are now working with the new implementation.

Categories

Resources