I have an application which communicates with a Bluetooth Low Energy Glucometer.
Currently my application is working OK on Android 4.4.4, but fails on 5.1.1
Debugging I've found that the writeDescriptor() method fails when executed more than once...
With the debugger I stepped into the writeDescriptor() method and found that it fails on this line:
public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
...
synchronized(mDeviceBusy) {
if (mDeviceBusy) return false;
mDeviceBusy = true;
}
(BluetoothGatt.java:1029)
I've tried to wait for the onDescriptorWrite() callback but it is never called, and I've also tried waiting (100 ms, 500ms and 2s) to do the second write, in order to see if the mDeviceBusy variable was cleared... Both attempts failed...
I googled the issue and could not find anything on it, the closest thing was this unanswered question: Android SensorTag: writeDescriptor failed
Note: I can attach my code if necessary, but it is very simple and similar to the code on this post: Android BLE notifications for Glucose
I found the problem... In my code I had defined the onDescriptorWrite() callback incorrectly. After fixing this I managed to receive callbacks when writing the descriptor succeeded.
With that solved, the solution I found is to make BTLE operations blocking by creating a class in charge of sequencing the operations.
The class I created is pretty simple, a BlockingQueue on a separate thread to enqueue incoming operations.
BTLE operations are enqueued by calling writeDescriptor(), writeCharacteristic() methods,a separate thread starts executing the operations when free, and the gattCallback informs the thread when operations have been completed, in order to proceed with the next operation.
Queue class:
public BtleAsynchronousOperationThread(SensorAbstract sensor, BluetoothGatt gatt) {
log("Creating Btle Operation thread");
this.gatt = gatt;
this.sensor = sensor;
}
public void enable() {
log("Enabling btle command thread");
queueReadySemaphore.release();
}
public void writeDescriptor(BluetoothGattDescriptor descriptor) throws BTCommunicationException {
BtleAsynchronousCommand command = new BtleAsynchronousCommand(descriptor);
try {
queue.put(command);
} catch (InterruptedException e) {
throw new BTCommunicationException("Error while writing the descriptor");
}
}
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) throws BTCommunicationException {
BtleAsynchronousCommand command = new BtleAsynchronousCommand(
BtleAsynchronousCommand.CommandType.WRITE_CHARACTERISTIC, characteristic);
try {
queue.put(command);
} catch (InterruptedException e) {
throw new BTCommunicationException("Error while writing the characteristic");
}
}
public void readCharacteristic(BluetoothGattCharacteristic characteristic) throws BTCommunicationException {
BtleAsynchronousCommand command = new BtleAsynchronousCommand(
BtleAsynchronousCommand.CommandType.READ_CHARACTERISTIC, characteristic);
try {
queue.put(command);
} catch (InterruptedException e) {
throw new BTCommunicationException("Error while reading the characteristic");
}
}
public void cancel() {
this.interrupt();
}
public void writeCompleted() {
queueReadySemaphore.release();
}
#Override
public void run() {
BtleAsynchronousCommand command;
try {
while (!(Thread.currentThread().isInterrupted())) {
queueReadySemaphore.acquire();
log("Waiting for BTLE command");
command = queue.take();
switch (command.getCommandType()) {
case WRITE_DESCRIPTOR:
log("Starting to write descriptor:" + command.getDescriptor().getUuid());
if (!gatt.writeDescriptor(command.getDescriptor())) {
throw new BTCommunicationException("Error while writing the descriptor");
}
break;
case WRITE_CHARACTERISTIC:
log("Starting to write characteristic:" + command.getCharacteristic().getUuid());
if (!gatt.writeCharacteristic(command.getCharacteristic())) {
throw new BTCommunicationException("Error while writing the characteristic");
}
break;
case READ_CHARACTERISTIC:
log("Starting to read characteristic:" + command.getCharacteristic().getUuid());
if (!gatt.readCharacteristic(command.getCharacteristic())) {
throw new BTCommunicationException("Error while writing the characteristic");
}
break;
default:
log("Unknown command received");
break;
}
}
} catch (InterruptedException e) {
log("Btle thread interrupted, closing.");
} catch (BTCommunicationException e) {
log("Error while reading:" + e.getMessage());
sensor.retry();
}
gatt = null;
queue = null;
queueReadySemaphore = null;
Thread.currentThread().interrupt();
}
}
When defining the BluetoothCallback:
mGattCallback = new BluetoothGattCallback() {
//Other methods can be defined around here, such as
//onConnectionStateChange(), etc
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
onSensorCharacteristicChangedInner(gatt, characteristic);
}
#Override
public void onDescriptorWrite (BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status){
log("onDescriptorWrite:"+descriptor.getUuid()+ " status:"+status);
btleCommunicationThread.writeCompleted();
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {
log("onCharacteristicWrite:"+characteristic.getUuid()+ " status:"+status);
btleCommunicationThread.writeCompleted();
}
Related
I have this code in Android Service:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!isCurrentlyRunning()) {
currentlyRunning = true;
ConnectivityManager connectivityManager = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder builder = new NetworkRequest.Builder();
connectivityManager.registerNetworkCallback(
builder.build(),
new ConnectivityManager.NetworkCallback() {
#Override
public void onAvailable(Network network) {
Log.d(TAG, "Start UpdateService after connectivity up");
try {
if (!UpdateService.isCurrentlyRunning()) {
if (Properties.DEBUG)
Log.d(TAG, "UpdateService is not running, try to start");
UpdateService.startService(getApplicationContext());
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
#Override
public void onLost(Network network) {
Log.d(TAG, "Stop UpdateService after connectivity lost");
try {
if (UpdateService.isCurrentlyRunning()) {
if (Properties.DEBUG)
Log.d(TAG, "UpdateService is running, sometimes need to redesigned stop service");
UpdateService.stopService(getApplicationContext());
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
);
}
return START_STICKY;
}
It would be all fine, but device is connected over DHCP and when onAvailable function is executed, its still not available DNS resolving.
How is the best way to wait (and check it during this waiting) for DNS resolver? I am thinking about for loop and sleep (and trying to connect for some web page during this loop), but during this sleeping is sleeping whole service and this solution I dont like it.
Thank you
D
I have some code that works, but just feels like a bad idea. I'm wondering if there's a better way to do this.
The Problem (You could honestly probably skip this and go to the code)
I have a bluetooth LE app that occasionally needs to write characteristics as fast as possible to multiple BLE devices (The app is a controller for a smart lamp). My current set up for BLE communication is to use a BluetoothContorller custom singleton which handles all communication, and Lamp objects which ask the controller to write to the lamps and hand off their BluetoothGatt and BluetoothGattCharacteristic to the controller.
The Solution
handler.post(new Runnable() {
#Override
public void run() {
if (control != null) {
synchronized (BluetoothController.this) {
Logger.d("BluetoothController", "Waiting......");
for(int i = 0; i < 100; i++){
if(isWaiting)
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
else
break;
}
isWaiting = true;
Logger.d("BluetoothController", "Done waiting.");
}
control.setValue(message);
mBluetoothGatt.writeCharacteristic(control);
}
}
});
And inside my gatt callback:
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
if(isWaiting){
isWaiting = false;
}
}
Now, this code works but all my experience as a programmer tells me that this code will cause me a lot of pain and surely there is a better.
EDIT: New Code using Handler.Callback and Messages
I've changed my code to do the waiting in the handle message callback, using Thread.wait() and notify(), but It seems like the messages are getting backed up in the queue, and the last one to execute still seems kind of random.
#Override
public void run() {
Looper.prepare();
handler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
synchronized (BluetoothController.this) {
if (shouldWait) {
isWaiting = true;
try {
BluetoothController.this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
isWaiting = false;
}
shouldWait = true;
}
return true;
}
});
Looper.loop();
}
I'm developing an application for Android 4.03. The code of relevance is this:
public void startConnection() {
new Thread(new Runnable() {
#Override
public void run() {
try {
Log.i(LOG_TAG, "Beginning");
_socket = new Socket(_server, _port);
_socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT);
_writer = new BufferedWriter(new OutputStreamWriter(_socket.getOutputStream()));
_reader = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
_in = new InputThread(_reader, new InputThreadObserver());
_in.start();
Log.i(LOG_TAG, "End");
} catch (UnknownHostException e) {
Log.i(LOG_TAG, "UnknownHostException");
} catch (IOException e) {
Log.i(LOG_TAG, "IOException");
}
}
}).start();
}
The creation of the socket is performed in a new thread, otherwise the execution freezes for few seconds.
If I set the variable _server to an existing host (for example www.google.com) everything goes all right. But if I set the _server variable to an host that does not exist (for example asd.asd) I really expect "UnknownHostException" to be printed in the logger. This does not happen (but the _socket variable is null). It just prints "Beginning" (and not "End"). Any Idea?
EDIT:
The variables are declared like this:
private String _server;
private Socket _socket;
private int _port;
private BufferedWriter _writer;
private BufferedReader _reader;
private InputThread _in;
EDIT:
I'm trying this:
public void startConnection() {
new Thread(new Runnable() {
#Override
public void run() {
try {
Log.i(LOG_TAG, "Beginning");
_socket = new Socket(_server, _port);
if (_socket == null)
Log.i(LOG_TAG, "NULL SOCKET! (test 1)");
} catch (Exception e) {
Log.i(LOG_TAG, "EXCEPTION!");
}
if (_socket == null)
Log.i(LOG_TAG, "NULL SOCKET! (test 2)");
}
}).start();
}
Don't know why but the output is only:
Beginning
EDIT:
After 3 minutes and 13 seconds waiting i finally got:
EXCEPTION!
NULL SOCKET! (test 2)
Is that normal? Shouldn't the UnknownHostException be thrown immediatly?
Looking at the API docs, if _server is a String, then you'll get an UnknownHost exception. If it's any of the other possibilities, you won't.
In particular this signature will create the exception:
Socket(String host,
int port)
throws UnknownHostException,
IOException
I have an app in java which is playing the rolle of a server .For limiting the number of incoming connections I'm using a ThreadPool server.
But I have a few problems understanding a part of the code:
Here is y code:
protected ExecutorService threadPool =
Executors.newFixedThreadPool(5);
public ThreadPooledServer(BlockingQueue queue,int port) {
this.serverPort = port;
this.queue=queue;
}
while (!isStopped()) {
Socket clientSocket = null;
try {
System.out.println("Serverul asteapta clienti spre conectare la port" +serverPort);
clientSocket = serverSocket.accept();
clientconnection++;
System.out.println("Serverul a acceptat clientul cu numarul:"
+ clientconnection);
} catch (IOException e) {
if (isStopped()) {
System.out.println("Server Stopped.");
return;
}
throw new RuntimeException("Error accepting client connection",
e);
}
WorkerRunnable workerRunnable = new WorkerRunnable(queue,clientSocket);
this.threadPool.execute(workerRunnable);
}
this.threadPool.shutdown();
System.out.println("Server Stopped.");
}
private synchronized boolean isStopped() {
return this.isStopped;
}
public synchronized void stop() {
this.isStopped = true;
try {
this.serverSocket.close();
}
catch (IOException e) {
throw new RuntimeException("Error closing server", e);
}
}
private void openServerSocket() {
try {
InetSocketAddress serverAddr = new InetSocketAddress(SERVERIP,
serverPort);
serverSocket = new ServerSocket();
serverSocket.bind(serverAddr);
} catch (IOException e) {
throw new RuntimeException("Cannot open port", e);
}
}
WHAT I don't understand:
I'm using a ThreadPooledServer which accepts for 5 incoming connections....
The connection with the clients is done in a while() loop.
while (!isStopped()) {
}
isStopped is a boolean variable returned byt this function:
private synchronized boolean isStopped() {
return this.isStopped;
}
which I call as a condition for starting the loop.
This boolean variable is initially set to false.....and is set back to true in the here:
public synchronized void stop() {
this.isStopped = true;
}
When is setup back to true my while() loop ends and then I close up all the workers of my thread pool.
this.threadPool.shutdown();
The problem is that I never call for this function " stop() "
Question: Is the function called automatically when I close my server?????...or I should call for it somewhere????
You need to call it somewhere in your code to stop your server and close those connections. If you don't the system will eventually reclaim its resources as the server will be shutting down.
You should be able to register a shutdown hook in the JVM (which can call stop()) to help with reclaiming those yourself... Good luck!
I'm writing an sample app to create a Server on Android and a client to connect to PC. I put the serversocket in a thread of a service. Everything goes perfectly, until a few minutes after the screen goes off. This may be Android kill my server, I tried to put a full wake lock to my code and it wont kill anymore, however, I DO want the screen go off as usual.
Here is my code:
public class MessageListener extends Service {
private ServerSocket serverSocket;
#Override
public void onCreate() {
Log.v("Test", "Create service");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
PowerManager.WakeLock wl=null;
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
startServer();
if(wl!=null) wl.release();
return START_STICKY;
}
private Runnable thread = new Runnable() {
#Override
public synchronized void run() {
try {
serverSocket = new ServerSocket(Integer.parseInt(5000));
ObjectInputStream in = null;
while (true) {
Socket client = serverSocket.accept();
Log.v("TCP", "S: Receiving...");
try {
in = new ObjectInputStream(client.getInputStream());
DataInController data = new DataInController(
getApplicationContext());
data.processDataIn(in.readObject(), client);
} catch (ClassNotFoundException e) {
System.out.println("TCP S: Error in PC Server Listener");
e.printStackTrace();
} finally {
client.close();
}
}
} catch (IOException e) {
}
}
};
private Thread serverThread;
private synchronized void startServer() {
if (serverThread == null) {
serverThread = new Thread(thread);
serverThread.start();
}
}
private synchronized void stopServer() {
if(serverThread!=null){
Thread t=serverThread;
serverThread=null;
t.interrupt();
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.v("TCP", "Killing Service!!!!!!!!!!!!!!!!!!!!!!!");
if (serverSocket != null) {
try {
serverSocket.close();
stopServer();
Log.v("TCP", "Closed server socket");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Two things that worked for us:
Wi-Fi lock
Set the Wi-Fi sleep policy to never. Some devices will power down the Wi-Fi radio without this setting, even when a program has a lock on the Wi-Fi radio.
I found the problem. That is the router lost the connection to Android. I've tried to ping it and it said "unreachable", after re connect to wifi, it works, but after a while, it comes again
Also try to keep WakeLock. Doing both works for me.