In my App, i have a Service including a Thread that ciclycally starts and stops BLE Advertising:
public class BeaconSearchService extends Service {
private void startAdvertisingThread() {
(advertisingThread = new Thread("Advertising Thread") {
#Override
public void run() {
//Ensure that advertising is off
try {
BeaconSearchService.this.bluetoothLeAdvertiser.stopAdvertising(BeaconSearchService.this.advertiseCallback);
} catch (Exception e) {
}
while(!isInterrputed()) {
AdvertiseSettings advertiseSettings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
.setConnectable(true)
.build();
BeaconSearchService.this.advertiseData.getServiceUuids().clear();
BeaconSearchService.this.advertiseData.getServiceUuids().add(BeaconSearchService.this.getUuid());
long uptime = 5000;
long downTime = 7000;
//START ADVERTISING
try {
BeaconSearchService.this.bluetoothLeAdvertiser.startAdvertising(advertiseSettings, BeaconSearchService.this.advertiseData, BeaconSearchService.this.advertiseCallback);
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
//SLEEP FOR 5 SEC
try {
sleep(upTime);
} catch (InterruptedException e) {
interrupt();
break;
}
//AFTER WAKE FROM SLEEP, STOP ADVERTISING
try {
BeaconSearchService.this.bluetoothLeAdvertiser.stopAdvertising(BeaconSearchService.this.advertiseCallback);
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
//SLEEP FOR 7 SEC
try {
sleep(downTime);
} catch (InterruptedException e) {
interrupt();
break;
}
//RESTART THE CIRCLE IF NOT INTERRUPTED
}
//Ensure stop advertising while exiting
try {
BeaconSearchService.this.bluetoothLeAdvertiser.stopAdvertising(BeaconSearchService.this.advertiseCallback);
} catch (Exception e) {
}
}).start();
}
#Override
public void onDestroy() {
try {
advertisingThread.interrupt();
} catch(Exception e) {
}
}
}
The problem is that when the Advertising is ON and Service and Thread are closed WITHOUT calling the onDestroy() method (e.g. App Crash, App process killed by the Local Android ROM Custom Behavior or while developing with Android Studio, App restart after new version download from Playstore etc), the Android system keeps the BLE Advertising ON. So when I reopen the App and sniff the BLE Packets I see TWO or More Advertising "sessions" of my App. The problem is solved when I reboot the phone and "orphan" Advertisig sessions disappear.
Is there any way to kill all the "zombie" Advertising sessions without forcing the user to reboot his phone?
Related
I have two devices connected through Bluetooth now. After that, I disconnected the Bluetooth connection on the Client device, and the broadcast receiver in this Client device can detect the disconnection, and then switch it back to previous activity. Something like this:
private BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Message msg = Message.obtain();
String action = intent.getAction();
if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
try {
Log.i("Disconnecting3", "Disconectinggg....");
Intent intent1 = new Intent(Main3Activity.this, MainActivity.class);
startActivity(intent1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
Anyhow, on my other device which is the Server device, this device CAN NOT detect the disconnection despite the Bluetooth socket is closed! The broadcast receiver in the Server device cannot detect the disconnection. FYI, below code will show how I close the Bluetooth socket on the Server device when the Client device is disconnected.
private boolean CONTINUE_READ_WRITE;
CONTINUE_READ_WRITE = true;
public void run() {
try {
while (CONTINUE_READ_WRITE) {
try {
// Read from the InputStream.
numBytes = mmInStream.read(mmBuffer);
// Send the obtained bytes to the UI activity.
Message readMsg = handleSeacrh.obtainMessage(MessageConstants.MESSAGE_READ, numBytes, -1, mmBuffer);
readMsg.sendToTarget();
} catch (IOException e) {
//nothing();
CloseConnection closeConnection = new CloseConnection();
closeConnection.start();
break;
}
}
} catch (Exception e) {
// Log.d(TAG, "Input stream was disconnected", e);
}
}
public void cancel() {
try {
Log.i("TAG", "Trying to close the socket");
CONTINUE_READ_WRITE = false;
mBluetoothSocket.close();
mmBluetoothSocket.close();
Log.i("TAG", "I thinked its still closing");
} catch (IOException e) {
Log.e("TAG", "Could not close the connect socket", e);
}
}
So when there is a disconnection happened on the Client device, the while(CONTINUE_READ_WRITE)..loop will break the loop and start a new Thread. Something like this :
private class CloseConnection extends Thread {
public void run(){
Log.i("Running","Runinnggggg");
try {
mmInStream.close();
mmOutStream.close();
bluetoothDataTransmission.cancel();
Log.i("Interrupted","InteruppteDDDD");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Alright, I found a solution , just need to add this line of code
intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
i built up an application based on aSmack, and obviously every thing about connection is running in a service, so it is very important to keep its connection Alive, as we know services running in background, may get killed upon phone's low sources (usually Ram) , so with the START_STICKY flag on the service, it restarts itself with a null intent.
now i wonder if there be no network on the phone ,or suddenly an unexpected temporary exception happenes in that time, (and because service is restarted reconnectionManager hasnt been set up yet), so the app must get restarted to retrieve its connection. my question is how can i handle these exceptions ? i know i can do some thing like this:
public void connect(){
try {
connection.connect();
} catch (SmackException | IOException | XMPPException e) {
if(getIntent() == null){
connect();
return;
}
}
}
but this is unprofessional imo, i know there was a way to determine temporary exceptions but unfortunanly i cant either remember or find them. any information is appreciated. thanks alot
so here is what i have done, works perfectly and every thing is counted in that.
this is awhat my service does every time it starts
private void connect(){
if (!connection.isConnected()){
try {
connection.connect();
} catch (SmackException | IOException | XMPPException e) {
mainHandler.post(new Runnable() {
#Override
public void run() {
if(intent ==null){
Toast.makeText(getBaseContext(),"Could not Connect to The Server , Network Problems , Retrying in 30 Seconds...", Toast.LENGTH_LONG).show();
}else{
Toast.makeText(getBaseContext(),"Could not Connect to The Server , Network Problems...", Toast.LENGTH_LONG).show();
}
}
});
if(intent == null){
//When intent is null, It Means that service got Destroyed middle of app, which
//means user has already connected and Authenticated once, but can not do it again.
//so thats the key
nonMainHandler.postDelayed(new Runnable() {
#Override
public void run() {
connect();
}
},30000);
}
e.printStackTrace();
}
}
try {
if (connection.isConnected() && !connection.isAuthenticated()) {
try {
connection.login(LMApplication.userName,LMApplication.passWord);
} catch (SmackException | IOException | XMPPException e) {
mainHandler.post(new Runnable() {
#Override
public void run() {
if(intent != null){
Toast.makeText(getBaseContext(),"Could not Login Using this Information..", Toast.LENGTH_LONG).show();
}
}
});
e.printStackTrace();
configEnterButton(-1);
}
}
if(connection.isAuthenticated()){
configEnterButton(100);
try {
Thread.sleep(300);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
goAhead();
Log.i("XMPPChatDemoActivity", "Logged in as" + LMApplication.Raw_CurrentUser);
//TODO
// check if need to set presence from shared preferences
Presence p = new Presence(Presence.Type.available,"", 42, Mode.available);
try {
connection.sendPacket(p);
} catch (NotConnectedException e1) {
e1.printStackTrace();
}
}else if(intent == null){
nonMainHandler.post(new Runnable() {
#Override
public void run() {
if(connection.isConnected() && !connection.isAuthenticated()){
try {
connection.login(LMApplication.userName,LMApplication.passWord);
} catch (XMPPException | SmackException
| IOException e) {
e.printStackTrace();
}
}
if(connection.isConnected() && connection.isAuthenticated()){
notifyReconnect();
}else if(connection.isConnected()){
nonMainHandler.postDelayed(this,10000);
}
}});
}
my question is how can i handle these exceptions ?
By performing a reconnect. Most applications I know do schedule the reconnect after a few seconds.
When another thread calls closeConnection(), the thread doesn't reach
Log.d("Subscriber", "Client thread has ended.");
Why is this? What is the blocking behaviour of a stream that has been closed? I thought trying to write or flush to it would generate an IOException, but it seems the code is still blocking somewhere. Where? I can't find info on what happens when you interrupt() on a write, or what happens when writing to a closed outputstream.
public void closeConnection() {
try {
this.interrupt();
autoCloseOutputStream.close();
} catch (IOException e) {
Log.w("Subscriber", "IOException when closing stream. Buffer might not have been flushed to client.");
}
}
#Override
public void run() {
Log.d("Subscriber","Client thread has started.");
ByteBuffer pgnAndDataBytes=null;
while(true) {
try {
pgnAndDataBytes=fmsByteBufferSubscriberQueue.take();
} catch (InterruptedException e) {
break;
}
Log.d("Subscriber","Still running thread");
try {
autoCloseOutputStream.write(pgnAndDataBytes.array());
autoCloseOutputStream.flush();
} catch (IOException e) {
break;
}
}
Log.d("Subscriber", "Client thread has ended.");
}
The output is as follows:
Still running thread
Still running thread
Still running thread
Close called.
And nothing more. Where is it blocking and why?
Have a volatile boolean shouldClose that you set to true on closeConnect(). Incorporate the boolean into the condition check of the while loop.
boolean done = false;
while(!shouldClose && !done) {
try{
autoCloseOutputStream.write(pgnAndDataBytes.getInt());
} catch(BufferUnderflowException bue) {
final ArrayList<Byte> remainder = new ArrayList<Byte>(3);
while(!shouldClose && !done) {
try {
remainder.add(pgnAndDataBytes.get());
} catch(BufferUnderflowException ex) {
autoCloseOutputStream.write(remainder.toArray(new Byte[remainder.size()]);
done = true;
}
}
}
}
I'm having issues with connecting. At first it works, than it does not, unless I unpair the devices.
I've gotten every possible exception that could happen, socket closed, pipe closed, connection refused, port already in use, etc.
I'm aware that there are issues with bluetooth on android pre 4.2 (https://code.google.com/p/android/issues/detail?id=37725).
Devices that I'm having problems with connecting these devices:
Htc one(android 4.2)
samsung galaxy s2(android 4.1.2)
nexus 4 (4.3)
samsung galaxy s4 (4.2)
Another minor issue is, that the paired devices are not stored (mostly on the nexus 4, and the sgs2).
Here is my code:
private static final UUID MY_UUID_SECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //this is the other one that I've tried: fa87c0d0-afac-11de-8a39-0800200c9a66");
private static final String NAME = "BluetoothConnector";
public void listenForConnection() throws IOException, BluetoothException {
//first close the socket if it is open
closeSocket();
BluetoothServerSocket mServerSocket = null;
try {
mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID_SECURE); //ioexception here!
} catch (IOException e) {
if (Build.VERSION.SDK_INT >= 9) {
try { //this is a stupid hack, http://stackoverflow.com/questions/6480480/rfcomm-connection-between-two-android-devices
Method m = mBluetoothAdapter.getClass().getMethod("listenUsingRfcommOn", new Class[] { int.class });
mServerSocket = (BluetoothServerSocket) m.invoke(mBluetoothAdapter, PORT);
} catch (Exception ex) {
Log.e(ex);
throw e;
}
} else {
throw e;
}
}
while (!isCancelled) {
try {
socket = mServerSocket.accept();
} catch (IOException e) {
if (socket != null) {
try {
socket.close();
} finally {
socket = null;
}
}
throw e;
}
if (socket == null) {
throw new BluetoothException("Socket connection connected, but null");
} else {
isConnected = true;
break; // everything is ok
}
}
}
public void connect(String address) throws IOException, BluetoothException {
mBluetoothAdapter.cancelDiscovery();
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
try {
socket = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
} catch (IOException e1) {
Log.e(e1);
if (Build.VERSION.SDK_INT >= 9) {
try {
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] { int.class });
socket = (BluetoothSocket) m.invoke(device, PORT);
} catch (Exception e) {
Log.e(e);
throw e1;
}
} else {
throw e1;
}
}
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
socket.connect();
} catch (IOException e) {
Log.e(e);
// Close the socket
try {
socket.close();
} catch (IOException e2) {
Log.e(e2);
Log.wtf("unable to close() socket during connection failure");
}
throw e;
}
}
private void closeSocket() {
try {
if (socket != null) {
socket.close();
socket = null;
Log.d("Socket closed");
}
} catch (IOException e) {
Log.e(e);
Log.wtf("close() of connect socket failed");
}
}
I tried changing the uuid(random one also), tried looking at older sdk samples.
So what could be wrong here?
edit: trying to clarify: the problem usually comes up, when 2 devices that have been paired, connected, did some successful communication, get disconnected (by the user). After that, they can not be reconnected, unless they get rebooted, or unpaired manually.
You are trying to paired this manner:
private void TwitPairedDevice() {
buttonTwitPairDevice.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Set<BluetoothDevice> fetchPairedDevices=bluetooth.getBondedDevices();
Iterator<BluetoothDevice> iterator=fetchPairedDevices.iterator();
while(iterator.hasNext())
{
final BluetoothDevice pairBthDevice=iterator.next();
final String addressPairedDevice=pairBthDevice.getAddress();
AsyncTask<Integer, Void, Void> asynchPairDevice=new AsyncTask<Integer, Void, Void>() {
#Override
protected Void doInBackground(Integer... params) {
try {
socket=pairBthDevice.createRfcommSocketToServiceRecord(uuid);
socket.connect();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
};asynchPairDevice.execute();
}
}
});
}
Connect Pired Device:
private void FetchPairedDevices() {
Set<BluetoothDevice> pairedDevices=bluetooth.getBondedDevices();
for(BluetoothDevice pairedBthDevice:pairedDevices)
{
listPairedDevice.add(pairedBthDevice.getName());
}
listviewPairedDevice.setAdapter(adapterPairedDevice);
listviewPairedDevice.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
Object listPairedName=arg0.getItemAtPosition(arg2);
String selectedPairedName=listPairedName.toString();
Set<BluetoothDevice> bthDeviceChecking=bluetooth.getBondedDevices();
for(final BluetoothDevice bthDevice:bthDeviceChecking)
{
if(bthDevice.getName().contains(selectedPairedName))
{
listPairDevice.clear();
listPairDevice.add(bthDevice);
final String addressPairedDevice=bthDevice.getAddress();
AsyncTask<Integer, Void, Void> asynTask=new AsyncTask<Integer,Void,Void>() {
#Override
protected Void doInBackground(Integer... params) {
try {
socket=bthDevice.createRfcommSocketToServiceRecord(uuid);
socket.connect();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
asynTask.execute(arg2);
}
}
}
});
}
It seems that at this point Bluetooth is broken on android.
There is no sure way of connecting 2 devices, that works all the time.
Some people are using an unofficial way to do it, but that does not work on all devices.
I did some in house testing, with the top 10 devices, that are on the market currently, so after around around 90 test runs, the hacked method worked 75% of the time, which is not good enough.
For example, the htc oneX will just handle incoming Bluetooth request, as a Bluetooth hands free device(it is connecting succesfully!), but makes messaging impossible.
After implementing full Bluetooth functionality, we decided to remove it from our app, and release it without it. We'll switch to wifi in some later release.
I have no idea what fails in this code because I have trouble reading the crash logs. We are not talking about a app-crash but a phone crash probably caused by either a deadlocked thread or a lock-up of some kind. Suggestions are welcomed!
Background:
When I initiate my connection a dialog shows and when I press the Back button the dialog freezes and after awhile the phone crashes...
Code:
This is the thread that handles the connection with the device. I have no issues connecting to a device at all. What I know is that mmSocket.connect() running when I press the back button. Think the problem lies there somewhere...
class ConnectThread extends Thread {
/**
*
*/
private Handler threadhandler;
private BluetoothDevice mmDevice;
private volatile BluetoothSocket mmSocket;
private Message toMain;
// private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
public ConnectThread(Handler threadhandler, BluetoothDevice device) {
this.threadhandler = threadhandler;
this.mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
Method m = mmDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
mmSocket = (BluetoothSocket) m.invoke(mmDevice, 1);
}catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public void run() {
Looper.prepare();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
toMain = threadhandler.obtainMessage();
toMain.arg1 = 1;
threadhandler.sendMessage(toMain);
} catch (SecurityException e) {
Log.e("SecurityExcep", "Oh noes" , e);
toMain = threadhandler.obtainMessage();
toMain.arg1 = 2;
threadhandler.sendMessage(toMain);
Log.w("MESSAGE", e.getMessage());
}catch (IOException e) {
//Bad connection, let's get the hell outta here
try {
Log.e("IOExcep", "Oh noes" , e);
Log.w("MESSAGE", e.getMessage());
mmSocket.close();
toMain = threadhandler.obtainMessage();
toMain.arg1 = 2;
toMain.obj = e.getMessage();
threadhandler.sendMessage(toMain);
return;
} catch (IOException e1) {
Log.e("IOExcep2", "Oh noes" , e);
}
}
try {
mmSocket.close();
} catch (IOException e) {
Log.d("CONNECT_CONSTRUCTOR", "Unable to close the socket", e);
}
toMain = threadhandler.obtainMessage();
toMain.arg1 = 3;
threadhandler.sendMessage(toMain);
Looper.loop();
return;
// Now it should be paired.. only thing to do now is let the user commit to the rest
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
Next code is a snippet from the dialog creator, the thread is called d:
(...)
case DIALOG_BT_ADDING:
search_dialog = new ProgressDialog(this);
search_dialog.setTitle(R.string.adding);
search_dialog.setMessage(res.getText(R.string.bluetooth_add_accept));
search_dialog.setIndeterminate(true);
search_dialog.setCancelable(true);
search_dialog.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
Log.i("THREAD CONNECT", "Is it alive?: " + d.isAlive());
if(d != null && d.isAlive()){
d.cancel();
//d = null;
}
if(d2 != null && d2.isAlive()){
d2.cancel(false);
//d2 = null;
}
search_dialog.dismiss();
showDialog(DIALOG_NEW_DEVICE_FOUND);
}
});
return search_dialog;
(...)
Here is a snippet of the code executing the ConnectThread-class
private void connectBluetooth(boolean nextstage, IOException e1){
if(!nextstage){
showDialog(DIALOG_BT_ADDING);
d = new ConnectThread(threadhandler, selected_car.getDevice());
d.start();
}
else{
if(e1 != null){
d2 = new BluetoothCheckThread(checkthreadhandler,mBluetoothAdapter,
5000, car_bt, after_bt);
d2.start();
search_dialog.dismiss();
}
else{
showDialog(DIALOG_BT_ADDING_FAILED);
}
}
}
Hope you guys can help me!, thanks for any feedback
Okay, you're calling BluetoothSocket.close() from what looks like the UI thread. This is probably causing the "freeze".
When you say the phone "crashes" do you mean it reboots? If so is this a full reboot (screen goes back to what happens when you first turn the phone on) or a runtime restart (phone typically shows some sort of animation, on Nexus devices, its the four-color particle spray)? If its not a reboot, do you mean you just get a dialog allowing you to kill the app?
In either case you may want to get a reference to the Thread that is calling BluetoothSocket.connect() and call Thread.interrupt(). I'm not sure if a BluetoothSocket is interruptible, but let's hope. Then after interrupting, call close(), which probably shouldn't be called on the main thread.
try to use dialog.setOnKeyListener() in that on keyCode_BACK cancel the thread. try this will work.
It looks like you're calling connect and close on BluetoothSocket which is a "no no". It seems to cause a deadlock. See this link for more info.