I have a Unity3D app with a Java plugin I've written in Android Studio for accessing a BluetoothLE server over on my Raspberry Pi 3 from my Android device, as well as an Objective-C plugin for XCode on the IOS side for CoreBluetooth. On the plugin side my Objective-C code works perfectly on IOS, but my Java crashes and burns on Android, and freezes the whole tablet, I'm not too surprised. Also notably, it's already taken me about 3x longer so far to code plugin support in Java. Maybe it's time to have a fresh set of eyes looking at it.
The difference is that CoreBluetooth's functions on IOS seem to read a GATT characteristic synchronously while Java requires asynchronous callbacks that don't seem to be working for me. It causes Unity and the entire tablet to hang until reboot.
Here is the part causing problems (it's two parts).
This first part is where I have Unity telling BluetoothLE to go start reading the pins (GetPins). I wanted it to basically act synchronously and wait until it's retrieved data so it can return a string like I've coded in the IOS version so I have it do a synchronized lock.
Basically Java is imposing an asynchronous coding model that's unnecessary on the IOS side (in fact, I'm not so sure there's an asynchronous way to code GATT characteristic reading on IOS CoreBluetooth, but I could be wrong), that would cause me to have to rewrite my entire plugin structure from Unity to support what Java's doing, which is going to be a lot more fiddly in the end and may break other things in the process.
public String GetPins() {
String returnValue = "";
if (mConnected) {
try {
if (getPinsCharacteristic == null) {
List<BluetoothGattService> services = mGatt.getServices();
if (mServiceRead == null) {
mGatt.discoverServices();
mServiceWrite = mGatt.getService(ServiceWriteUUID);
mServiceRead = mGatt.getService(ServiceReadUUID);
mServiceNotify = mGatt.getService(ServiceNotifyUUID);
}
getPinsCharacteristic = mServiceRead.getCharacteristic(PinsCharacteristic);
}
} catch(Exception ex) {
Log.i("MERRRRRP",String.format("Exception: (%s)",ex.toString()));
}
Log.i("MERRRRRP", "Reading Pins");
if (getPinsCharacteristic != null) {
try {
boolean success = mGatt.readCharacteristic(getPinsCharacteristic);
isReadingPins = true;
while(isReadingPins) {
synchronized (readLockPins) {
try {
readLockPins.wait(1);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
}
returnValue = new String(getPinsCharacteristic.getValue(), "UTF-8");
}
catch(Exception ex) {
returnValue = ex.toString();
}
}
}
return returnValue;
}
Now this is the part onCharacteristicRead where it's actually getting the result back, and should be unlocking the synchronized lock and I should get the value back. Instead the whole tablet is freezing.
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
if (characteristic.equals(getPinsCharacteristic)) {
synchronized (readLockPins) {
isReadingPins = false;
}
}
}
}
Your first block of code acquiring lock in a loop and holding it for one second. You assume that your second block of code will execute and acquire the lock between those iterations in first block.
That assumption is wrong. The locks are not fair. If thread A is acquiring and releasing a lock in the loop, and thread B is trying to acquire that same lock, there is not guarantee, that thread B gets it in reasonable time (or maybe never).
The second potential problem (I cannot determine from the code you just posted) is that the variable isReadingPins in while(isReadingPins) MUST be volatile.
Variables not marked volatile do not trigger memory barrier operation, which results in the scenario where writing to the variable in one thread is not visible in another thread.
In your case, isReadingPins = false; in second code block will probably not be visible to the thread running the first code block, because value of the variable will in cache local to the CPU core running that code block, which will always be true, because the write to false is local to another CPU core and CPU core caches will not be synchronized due to absence of memory barrier. This is why the variable must be volatile.
In the end, this is pretty clumsy way of doing this. I don't know Android API, but in regular Java this would be solved using a promise/future or at least a simple Latch.
edit And another thing... if your read function fails, or returns something that is not GATT_SUCCESS, then isReadingPins is never set to false and your application just freezes. This is bad design.
Related
I have a "ClientsHandler" class that reads client input and converts it into a server command.
The problem is that when I use the exit command in the server console, runFlag is set to false and the loop starts waiting for the last input from the client before closing the thread. How can I break the loop and stop the thread instantly after exit command?
class ClientsHandler(
private val socket: Socket,
private val commandManager: CommandManager
) : Runnable {
companion object {
var runFlag = true
fun close() {
runFlag = false
}
}
override fun run() {
var input = ObjectInputStream(socket.getInputStream())
var output = ObjectOutputStream(socket.getOutputStream())
var username = (input.readObject() as User).username
println("$username connected")
while (runFlag) {
try {
var request = input.readObject() as ServerRequest
output.writeObject(ServerCommandInvoker.invoke(request, commandManager))
} catch (e: Exception) {
println("$username disconnected")
runFlag = false
}
}
input.close()
output.close()
socket.close()
}
}
Two options.
Interrupt
This is a tricky one, in that the Java library spec explicitly reserves the right for this not to work. On most platforms it does work, but you should probably skip it and go for the second option below.
The idea is, theThreadThatIsCurrentlyBlockingOnTheReadObjectCall.interrupt() will generally cause that readObject() call to exit immediately with an IOException whose message states something along the lines of 'thread interrupted'. You can now continue.
The reason this is not guaranteed is because java needs to run on a very wide array of platforms and hardware and perhaps not all of them can do this.
Just close the socket
Instead, from the thread that is setting the runFlag to false, you can also just close the socket directly. This will, definitely, cause that readObject() call that's still running to exit immediately with an IOException indicating that the socket is now closed.
Note that your setting of the runFlag appears invalid to me, in that it doesn't use volatile or synchronized or some other mechanism to ensure this field update is actually visible from the other thread reliably. the JVM is free to make that runFlag change be visible to your ClientsHandler run method, or not - JVM's choice. So you can't even test that your code is broken.
Generally for purposes like this, you can use an AtomicBoolean, which guarantees that writes in one thread are visible to another without a potentially very long delay.
I have an issue leading to the requirement of needing to wait sequentially from going from one thing into the next. I presently do this by setting 3 runnables with different delays to allow for a sequential flow of data to appear on my bluetooth connection. However, whilst this does work I feel there must be a better / cleaner implementation of this. My present code is below.
My code works like this:
Write command 1
Wait till command is written
Write command 2
Wait till command is written
Write command 3
Wait till command is written
Please could you give me some suggestions as to how I can perform my write functions one after another in a better manner.
Handler h =new Handler() ;
h.postDelayed(new Runnable() {
public void run() {
Log.d(TAG, "Write 1");
mBluetoothLeService.writeCharacteristic(10);
}
}, 1000);
Handler h1 =new Handler() ;
final int Command_to_run = value;
h1.postDelayed(new Runnable() {
public void run() {
Log.d(TAG, "Write 2");
mBluetoothLeService.writeCharacteristic(Command_to_run);
}
}, 2000);
Handler h2 =new Handler() ;
h2.postDelayed(new Runnable() {
public void run() {
Log.d(TAG, "Write 3");
mBluetoothLeService.writeCharacteristic(20);
}
}, 3000);
Write code
public void writeCharacteristic(int Data) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
byte[] value = intToByteArray(Data);
BluetoothGattService mCustomService =
mBluetoothGatt.getService(UUID.fromString("f3641400-00b0-4240-ba50-
05ca45bf8abc"));
if(mCustomService == null){
Log.w(TAG, "Custom BLE Service not found");
return;
}
/*get the read characteristic from the service*/
BluetoothGattCharacteristic characteristic =
mCustomService.getCharacteristic(UUID.fromString("f3641401-00b0-4240-
ba50-05ca45bf8abc"));
characteristic.setValue(value);
mBluetoothGatt.writeCharacteristic(characteristic);
}
I think mBluetoothLeService.writeCharacteristic(10); calls like these already blocks the thread so using them in order without the need of handlers can be your solution. I don't think that function is asynchronous, so if it returns true, you can write the next one. They are boolean functions so if it returns true you can switch to next one.
I've examined the source code and if it throws an exception inside it, it returns false. Otherwise, it returns if it was successful or not.
Side note: This behavior might differ on different API versions, the source code I've looked into was for API 29. Though, I believe the behavior would be the same, except you might need to wrap the mBluetoothLeService.writeCharacteristic(10); calls to a try-catch block.
I have to edit this since the answer is wrong, the boolean return value is not enough to determine whether the operation was successful. The operation is indeed asynchronous but there is a callback that you can use (this callback) to see if the write operation was successful, and then move on to the next one.
Check this answer for more information, and, if possible, remove the tick from this one please.
Android's BLE API is fully asynchronous and there are no blocking methods. The methods will return true if the operation was initiated successfully and false otherwise. In particular, false will be returned if there already is an operation ongoing.
When you call requestMtu, writeCharacteristic, readCharacteristic and so on, the corresponding callback onMtuChanged, onCharacteristicWrite, onCharacteristicRead will be called when the operation is complete. Note that this usually means a roundtrip to the remote device, which might take different amount of time to complete depending on how noisy the environment is and which connection parameters you have, so there is never a good idea to sleep or delay some fixed amount of time and assume the operation has then completed.
To make the code structure a bit better and avoiding the "callback hell", you could for example implement a (thread-safe) GATT queue which is later used by your application. This way you can just push what you want in the queue and let your GATT queue library handle the dirty stuff. Or you can use some Android BLE library that already does this.
See https://medium.com/#martijn.van.welie/making-android-ble-work-part-3-117d3a8aee23 for a more thorough discussion.
If the work you want to perform sequentially can be done asynchronously, you can consider the new WorkManager included in Android Jetpack. With WorkManager you can organize all your work very smartly, as per the documentation you can do it as follows:
WorkManager.getInstance(myContext)
// Candidates to run in parallel
.beginWith(listOf(filter1, filter2, filter3))
// Dependent work (only runs after all previous work in chain)
.then(compress)
.then(upload)
// Don't forget to enqueue()
.enqueue()
The library takes good care of the order of the execution for you. You can find more information on this matter here: https://developer.android.com/topic/libraries/architecture/workmanager/how-to/chain-work
I currently have the following problem:
I have made a 'Cache Updater Thread', which checks for updates and then sleeps for some amount of time. I have also build a Button, which enables the user to check for updates manually. The Thread is built like this:
public static Thread cacheUpdater = new Thread(new Runnable() {
int milliSecondSleepTime = 10000;
public void run() {
try {
cacheUpdater.setPriority(Thread.MIN_PRIORITY);
//Infinite loop
while (!terminate) {
syncStatus.set(0);
//Check for updates with some methods, not important here.
syncStatus.set(1);
Thread.sleep(this.milliSecondSleepTime);
}
}
catch (InterruptedException e) {
//First check if it is termination time
if (!terminate) {
syncStatus.set(0);
this.run();
}
}
catch (Exception e) {
System.out.println(e);
}
return;
}
});
If the user clicks the manual-update button, the following code is being runned:
#FXML public void syncOnRequest() {
//Only call interrupt, because then it will start again when terminate is still false
CacheManager.cacheUpdater.interrupt();
System.out.println(CacheManager.cacheUpdater.getState().equals(State.TIMED_WAITING));
while (!CacheManager.cacheUpdater.getState().equals(State.TIMED_WAITING)) {
//LOOP FOREVER
}
//Some code that needs to be executed after the cache is updated
}
I would like to continue executing code in the syncOnRequest() method, when the cache updater is ready with its manual update. I had the idea to check if it is sleeping, but this is not working, because the System.out.println() immediately returns true. I have measured the time it takes to do the update, and its between 200 and 400 ms.
What am I doing wrong here? And why is it always returning true?
Additional question: sometimes a click on the button just kills the Thread, because it just woke up. The InterruptedException is not thrown.
How can I make sure the Thread will also restart in that case?
Note that Thread#interrupt() is the only polite way to ask your thread to interrupt itself (unless you explicitly implement another). Using it to restart the check is therefore a bad practice. So is checking the thread state for synchronization purposes and exposing the thread that keeps your cache up-to-date to external clients.
You manager should have a updateCache() method you will call directly from UI code and auto-update thread will call the same method periodically*. In that method, make sure that access to your cached data is either correctly synchronized or it happens atomically.
*) Instead of implementing your own periodic thread, consider using
Timer and TimerTask classes as well as making it a daemon thread.
I've got some problems with Android Bluetooth stuff.
When I call
bytes = mmInStream.read(buffer);
It usually works as it should.
On the Cat B15 smartphone however, the read method sometimes blocks forever, even though the connection is still running and data should be arriving.
I have temporarily solved the problem by this code:
while (true) {
int available = 0;
try {
available = mInStream.available();
} catch (IOException e) {}
if (available > 0) {
try {
bytes = mInStream.read(buffer);
ioExceptionsCounter = 0;
// [send the obtained bytes to the UI activity]
// ...............
} catch (IOException e) {
++ioExceptionsCounter;
if (ioExceptionsCounter >= 4) {
break;
}
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
}
I dont think that the ioExceptionsCounter is really necessary but there was some complaints that it sometimes disconnects without reason so I thought one IOException might not be enough to close the connection.
What I really don't like about this is that it uses polling. It does work now on the Cat phone but it doesn't make me happy that all the other devices now execute this ugly code.
Do you have any ideas why this could be? Is this just a bug of the phone? By the way it runs ICS but it is definitely device specific.
I'm inclined to think that you are encountering a hardware-specific bug.
The various InputStream.read() methods are documented to block until at least one byte is read, or the end of the stream is detected, or an error occurs. If the read() sometimes blocks forever for you in the event that no bytes are available when it is first invoked, then that's definitely a bug outside your own code.
Also, it's highly questionable to ignore any number of IOExceptions, either from available() or from read(). After the stream throws an exception you cannot be confident of the integrity of anything you afterward might manage to read from it. I would normally expect such attempts at further reading also to throw IOExceptions. If you are getting spurious IOExceptions on the Cat B15, such that simply retrying your read() successfully obtains the correct data, then that is also a bug (maybe another facet of the same one).
I have 2 classes where I have one to send commands to a socket, another which receives (and sometimes answers commands) .
Do I have to synchronize this? Or is this not necessary?
Both classes run in their own threads with the socket object passed down to each of them as argument upon thread.start();
Is this the proper way to do it or could I do something more efficient?
Would this have chances of causing errors?
The sending part:
public void run(){
send_chatline("roomf");
int vvv = 0;
while (this.socket.isConnected()){
try{
Thread.sleep(10000);
vvv++;
Thread.sleep(10000);
send_chatline("alive");
Thread.sleep(10000);
if (vvv == 1) {
this.socket.flush();
this.socket.send("T,-1," + this.playerid * 3);
this.socket.flush();
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
But remember! The recieveFromSock class also writes sometimes when specific commands appear.
The only function of sendTosock is to keep the connection alive (being able to remain online).
The only guarantee you have is that a byte sent, is sent once, and that a byte read is read once. Others may say that the synchronization is taken care of by the runtime-library, or the underlying operating system. Any such statement is runtime-implementation dependent and/or operating system dependent (and should in my opinion be disregarded from).
So, if you have one thread reading from the socket, and one thread writing to the socket, you should be fine without synchronization.
If more than one thread may write to the socket, you should synchronize their actions to make sure the output sent from the two threads doesn't get interleaved.
Same applies if two threads read from the socket. You should synchronize the reads to make sure that data read by one thread doesn't have "gaps" due to reads of another thread.
Similar question, same conclusion:
in what way is java.net.Socket threadsafe?
Do I have to synchronize this?
No. SocketImpl has its own internal synchronization. You won't be surprised to learn that sending isn't synchronized but receiving is.
You don't need to synchronize, as the synchronization is done on the socket. The question is: are the two classes/threads communicating through sockets living in the same JVM? If so, there are several more efficient options, the most simple being having a BlockingQueue where the sender class adds its commands and the receiver takes them.
I was curious too for the nio classes and it IS synchronized(for nio only). Just look at the write method here in the sun JVM code
http://www.docjar.com/html/api/sun/nio/ch/SocketChannelImpl.java.html
Checking the old i/o socket code however shows no synchronization though there is an acquireFD and releaseFD...but no sycnhronization there...that appears to prevent a close until all writes on all threads are done(it is like they assume writes from multiple threads are okay in every OS...but I don't know if that is true OR they synchronize in the JVM native code so the developer knows they can do that)....we would need a JVM developer to tell us if there is a synch block in the windows JVM, linux JVM and mac JVM, etc. etc....
private void socketWrite(byte b[], int off, int len) throws IOException {
if (len <= 0 || off < 0 || off + len > b.length) {
if (len == 0) {
return;
}
throw new ArrayIndexOutOfBoundsException();
}
FileDescriptor fd = impl.acquireFD();
try {
socketWrite0(fd, b, off, len);
} catch (SocketException se) {
if (se instanceof sun.net.ConnectionResetException) {
impl.setConnectionResetPending();
se = new SocketException("Connection reset");
}
if (impl.isClosedOrPending()) {
throw new SocketException("Socket closed");
} else {
throw se;
}
} finally {
impl.releaseFD();
}
}