Bit of an odd one, but I'm having issues with monitoring the connection status of an XMPP connection. I'm using Smack (3.4.1) and Openfire (3.8.2)
Openfire reports that the client has disconnected. As does my remote client BOT, which is responsible for middle-man server requests. However, the devices themselves still believe they're connected. So something has abruptly killed the connection.
I can replicate the scenario, by closing the client connection manually via Openfire admin console. After doing this, the client still reports that it's connected, with the following details:
04-15 09:48:23.349: D/BXC(18166): Checking Xmpp connection status
04-15 09:48:23.349: I/BXC(18166): Xmpp Connection still valid
04-15 09:48:23.349: D/BXC(18166): isNull=false
04-15 09:48:23.349: D/BXC(18166): isConnected()=true
04-15 09:48:23.349: D/BXC(18166): isAuthenticated()=true
04-15 09:48:23.349: D/BXC(18166): xmppConnectorRunning=true
04-15 09:48:23.349: D/BXC(18166): socketIsClosed()=false
04-15 09:48:23.349: D/BXC(18166): Are we logged in?
04-15 09:48:23.349: I/BXC(18166): Yes - Logged in.
From the code:
Log.d("BXC", "isNull=" + (xConnection == null));
Log.d("BXC", "isConnected()=" + xConnection.isConnected());
Log.d("BXC", "isAuthenticated()=" + xConnection.isAuthenticated());
Log.d("BXC", "xmppConnectorRunning=" + Globals.backgroundXmppConnectorRunning);
Log.d("BXC", "socketIsClosed()=" + xConnection.isSocketClosed());
The trouble is, that the connection will remain in this state until I restart the app. What is the best value/data/variable/method call to use to check the actual status of an XMPP connection?
Here's my BackgroundXmppConnector Service code (I've quite possibly borked something along the way, but I can't see it):
package com.goosesys.gaggle.services;
import java.util.Collection;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import com.google.gson.Gson;
import com.goosesys.gaggle.Globals;
import com.goosesys.gaggle.application.AppSettings;
import com.goosesys.gaggle.application.Utility;
import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
public class BackgroundXmppConnector extends Service
{
private ConnectionConfiguration acc;
private XMPPConnection xConnection;
private final IBinder mBinder = new XmppBinder();
private final Handler mHandler = new Handler();
private static int mInterval = (1 * 60 * 1000);
private static boolean bConnecting = false;
private static final Object connectLock = new Object();
private final Runnable checkConnection = new Runnable()
{
@Override
public void run()
{
checkConnectionStatus();
}
};
@Override
public void onCreate()
{
Log.i("BXC", "BackgroundXmppConnector Service has been created");
// Checks the connection state every 1 minute //
mHandler.postDelayed(checkConnection, mInterval);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// SmackAndroid.init(getApplicationContext());
Log.d("BXC", "Xmpp Connector Started");
if(xConnection == null)
{
checkConnectionStatus();
}
return Service.START_STICKY;
}
private void setupConnection()
{
try
{
acc = new ConnectionConfiguration(AppSettings.XMPP_SERVER_HOST,
AppSettings.XMPP_SERVER_PORT);
acc.setSecurityMode(SecurityMode.disabled);
acc.setSASLAuthenticationEnabled(false);
acc.setReconnectionAllowed(true);
xConnection = new XMPPConnection(acc);
xConnection.addConnectionListener(new ConnectionListener()
{
@Override
public void connectionClosed()
{
Log.e("BXC", "Xmpp connection closed");
Globals.backgroundXmppConnectorRunning = false;
bConnecting = false;
}
@Override
public void connectionClosedOnError(Exception e)
{
Log.e("BXC", "Xmpp connection closed with error: " + e);
Globals.backgroundXmppConnectorRunning = false;
bConnecting = false;
}
@Override
public void reconnectingIn(int seconds)
{
Log.i("BXC", "Xmpp connection, reconnecting in " + seconds + " seconds");
bConnecting = true;
}
@Override
public void reconnectionFailed(Exception e)
{
Log.e("BXC", "Xmpp reconnection failed: " + e);
Globals.backgroundXmppConnectorRunning = false;
bConnecting = false;
}
@Override
public void reconnectionSuccessful()
{
Log.i("BXC", "Xmpp reconnected successfully");
Globals.backgroundXmppConnectorRunning = true;
bConnecting = false;
}
});
}
catch (Exception e)
{
Log.e("BXC", e.getMessage());
}
finally
{
// Schedule another check in 1 minute //
mHandler.postDelayed(checkConnection, mInterval);
}
if(xConnection.isAuthenticated() == false)
new LoginTask().execute();
}
public boolean sendMessage(Intent intent)
{
if(xConnection != null && xConnection.isConnected())
{
String jsonObject;
Bundle extras = intent.getExtras();
if(extras != null)
{
jsonObject = extras.getString("MESSAGEDATA");
Message m = new Gson().fromJson(jsonObject, Message.class);
if(m != null)
{
sendMessage(m);
}
else
{
Log.e("BXC", "Message to send was/is null. Can't send.");
}
m = null;
jsonObject = null;
extras = null;
}
Log.i("BXC", "Sending Xmpp Packet");
return true;
}
return false;
}
/*
* Sends message to xmpp server - message packet in form of
*
* --------------------MESSAGE PACKET-------------------------
* TO
* -----------------------
* FROM
* -----------------------
* BODY
* TRANSACTION-------------------------------------------
* MessageType
* --------------------------------------------------
* TransactionObject
*/
private void sendMessage(Message m)
{
try
{
Log.d("BXC", "Sending transaction message to Xmpp Server");
xConnection.sendPacket(m);
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
public boolean isConnected()
{
return xConnection.isConnected();
}
private void checkConnectionStatus()
{
Log.d("BXC", "Checking Xmpp connection status");
if(!bConnecting)
{
// if connection object is null - re-create
if(xConnection == null) // || xConnection.isConnected() == false
{
// The connection has stalled for some reason - attempt a reconnect
Log.e("BXC", "No connection. Attempting to connect.");
setupConnection();
}
else
{
Log.i("BXC", "Xmpp Connection still valid");
}
Log.d("BXC", "isNull=" + (xConnection == null));
Log.d("BXC", "isConnected()=" + xConnection.isConnected());
Log.d("BXC", "isAuthenticated()=" + xConnection.isAuthenticated());
Log.d("BXC", "xmppConnectorRunning=" + Globals.backgroundXmppConnectorRunning);
Log.d("BXC", "socketIsClosed()=" + xConnection.isSocketClosed());
if(xConnection != null && xConnection.isConnected() == true)
{
Log.d("BXC", "Are we logged in?");
if(xConnection.isAuthenticated() == false)
{
Log.e("BXC", "Nope. Logging in...");
new LoginTask().execute();
}
else
{
Log.i("BXC", "Yes - Logged in.");
}
}
else if(xConnection == null || xConnection.isConnected() == false || xConnection.isAuthenticated() == false)
{
Log.e("BXC", "Disconnected. Attempting reconnect");
new LoginTask().execute();
}
else
{
Log.e("BXC", "Couldn't log in because connection failed.");
}
}
else
{
Log.e("BXC", "Already checking.");
}
// Schedule again for 5 minutes.
mHandler.postDelayed(checkConnection, mInterval);
}
// BINDER ////////////////////////////////////////////////////////////////////////////////
@Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
// INTERNAL CLASSES //////////////////////////////////////////////////////////////////////
public class XmppBinder extends Binder
{
public BackgroundXmppConnector getService(){
return BackgroundXmppConnector.this;
}
}
private class LoginTask extends AsyncTask<Void, Void, Void>
{
@Override
protected Void doInBackground(Void... params)
{
try
{
bConnecting = true;
synchronized(connectLock)
{
if(xConnection != null && (xConnection.isSocketClosed() || !xConnection.isConnected()))
{
xConnection.connect();
Log.i("BXC", "Login Credentials: " + Utility.getAndroidID(getApplicationContext()) + " / " + AppSettings.XMPP_KEYSTORE_PASSWORD);
xConnection.login(Utility.getAndroidID(getApplicationContext()), AppSettings.XMPP_KEYSTORE_PASSWORD);
xConnection.getChatManager().addChatListener(new ChatManagerListener(){
@Override
public void chatCreated(final Chat chat, boolean createdLocally)
{
if(!createdLocally)
{
// add chat listener //
chat.addMessageListener(new BackgroundMessageListener(getApplicationContext()));
}
}
});
Presence p = new Presence(Presence.Type.subscribe);
p.setStatus("Out and About");
xConnection.sendPacket(p);
Roster r = xConnection.getRoster();
r.setSubscriptionMode(SubscriptionMode.accept_all);
r.createEntry(AppSettings.BOT_NAME, "AbleBot", null);
r.addRosterListener(new RosterListener(){
@Override
public void entriesAdded(Collection<String> addresses)
{
for(String s : addresses)
{
Log.d("BXC", "Entries Added: " + s);
}
}
@Override
public void entriesDeleted(Collection<String> addresses)
{
for(String s : addresses)
{
Log.d("BXC", "Entries Deleted: " + s);
}
}
@Override
public void entriesUpdated(Collection<String> addresses)
{
for(String s : addresses)
{
Log.d("BXC", "Entries updated: " + s);
}
}
@Override
public void presenceChanged(Presence presence)
{
Log.d("BXC", "PresenceChanged: " + presence.getFrom());
}
});
}
}
}
catch(IllegalStateException ex)
{
Log.e("BXC", "IllegalStateException -->");
Globals.backgroundXmppConnectorRunning = false;
ex.printStackTrace();
}
catch(XMPPException ex)
{
Log.e("BXC", "XMPPException -->");
Globals.backgroundXmppConnectorRunning = false;
ex.printStackTrace();
}
catch(NullPointerException ex)
{
Log.e("BXC", "NullPointerException -->");
Globals.backgroundXmppConnectorRunning = false;
ex.printStackTrace();
}
catch(Exception ex)
{
Log.e("BXC", "Exception -->");
Globals.backgroundXmppConnectorRunning = false;
ex.printStackTrace();
}
return null;
//}
}
@Override
protected void onPostExecute(Void ignored)
{
if(xConnection != null)
{
if(xConnection.isConnected() && (!xConnection.isSocketClosed()))
{
Log.i("BXC", "Logged in to XMPP Server");
Globals.backgroundXmppConnectorRunning = true;
bConnecting = false;
}
else
{
Log.e("BXC", "Unable to log into XMPP Server.");
Globals.backgroundXmppConnectorRunning = false;
bConnecting = false;
}
}
else
{
Log.e("BXC", "Xmpp Connection object is null");
Globals.backgroundXmppConnectorRunning = false;
bConnecting = false;
}
}
}
}