0
votes

I am trying to write chat application with smack/xmpp. I installed ejabberd server. My programm connect to it well and can to send messages to other clients. XMPP service has STICKY property. Problem appear after run heavy applications like 3d games. During playing game, xmpp connection to server broked, after exiting game the service automatically restarts and trying to connect again, but SMACKException appear:

09-20 15:46:06.540 31467-31533/home.chat E/(onCreate):SMACKException: The following addresses failed: '94.x.x.x:5222' failed because java.net.ConnectException: failed to connect to /94.x.x.x (port 5222) after 30000ms: isConnected failed: ECONNREFUSED (Connection refused)

I read explanation of this error, it means: Signals that an error occurred while attempting to connect a socket to a remote address and port. Typically, the connection was refused remotely (e.g., no process is listening on the remote address/port).

In my xmpp service I have Reconnection Manager. It works fine in some cases, but it nothing do in this case.

After catching this exception I tried programmatically restart service, but this not helps. Only manual unloading and restarting application helps. I think that server after timeout 30s close xmpp connection, but why reloading service not give result? Does anyone know if something is wrong in my code?

MyService class package home.chat;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

import java.io.File;
import java.util.Date;

public class MyService extends Service {
private static String DOMAIN = GlobalVariables.server;
private static String USERNAME = GlobalVariables.user_id;
private static String PASSWORD = GlobalVariables.user_password;
public static MyXMPP xmpp;
String text = "";
private LocalDb ldb;
private Boolean disconnectAppeared = false;
BroadcastReceiver br = null;
Date d1 = null;
Date d2 = null;
static MyService instance;

public static MyService getInstance(){
    return instance;
}

@Override
public IBinder onBind(final Intent intent) {
    return new LocalBinder<MyService>(this);
}

@Override
public void onCreate() {
    super.onCreate();
    instance = this;
    checkInternetConnection();
    xmpp = MyXMPP.getInstance(MyService.this, DOMAIN, USERNAME, PASSWORD);
    connect();

    Log.e("MyService"," created");
}

public static void connect(){
    xmpp.connect("onCreate");
}

@Override
public int onStartCommand(final Intent intent, final int flags,
                          final int startId) {
    return Service.START_STICKY;
}

@Override
public boolean onUnbind(final Intent intent) {
    return super.onUnbind(intent);
}

@Override
public void onDestroy() {
    super.onDestroy();
    Log.e("MyService"," destroyed");
    xmpp.disconnect();
    unregisterReceiver(br);
    Log.i("EXIT", "ondestroy!");
    Intent broadcastIntent = new Intent("home.chat.ActivityRecognition.RestartService");
    sendBroadcast(broadcastIntent);
}

private void checkInternetConnection() {
    if (br == null) {
        br = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                Bundle extras = intent.getExtras();
                NetworkInfo info = (NetworkInfo) extras
                        .getParcelable("networkInfo");
                NetworkInfo.State state = info.getState();
                Log.d("TEST Internet", info.toString() + " "
                        + state.toString());
                if (state == NetworkInfo.State.CONNECTED & disconnectAppeared) { //on
                        xmpp.connect("After network changes");
                    disconnectAppeared = false;
                }
                if (state == NetworkInfo.State.DISCONNECTED) { //off
                    disconnectAppeared = true;
                }
            }
        };
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(br, intentFilter);
    }
}
public void destroyService(){
    stopSelf();
}
}

MyXMPP class

package home.chat;

import *;

public class MyXMPP {

public static boolean connected = false;
public static boolean loggedin = false;
public static boolean isconnecting = false;
public static boolean isToasted = false;
private boolean chat_created = false;
private String serverAddress;
public static XMPPTCPConnection connection;
public static String loginUser;
public static String passwordUser;
Gson gson;
static MyService context;
public static MyXMPP instance = null;
public static boolean instanceCreated = false;
private Handler mHandler = new Handler();
public static ReconnectionManager connMgr;
int[] rt_arr = {2,2,2,5,5,10,10};
int curr_delay = 0;
public static ConnectivityManager cm = null;
public NetworkInfo activeNetwork = null;
public static Roster myRoster;

static ArrayList<ChatMessage> msg_array = new ArrayList<ChatMessage>(); //буфер сообщений для отправки
public static ArrayList<HashMap<String,String>> msg_queue = new ArrayList<>();
public static ArrayList<HashMap<String,String>> stat_list = new ArrayList<>();
public String senderName = "";

public MyXMPP(MyService context, String serverAdress, String logiUser,
              String passwordser) {
    this.serverAddress = serverAdress;
    this.loginUser = logiUser;
    this.passwordUser = passwordser;
    this.context = context;
    init();
}

public static MyXMPP getInstance(MyService context, String server,
                                 String user, String pass) {
    if (instance == null) {
        instance = new MyXMPP(context, server, user, pass);
        instanceCreated = true;
        Log.e("MyXMPP","create new instance");
    }
    return instance;
}

public org.jivesoftware.smack.chat.Chat Mychat;

ChatManagerListenerImpl mChatManagerListener;
MMessageListener mMessageListener;

String text = "";
String mMessage = "", mReceiver = "";

static {
    try {
        Class.forName("org.jivesoftware.smack.ReconnectionManager");
    } catch (ClassNotFoundException ex) {
        Log.e("E:","problem loading reconnection manager");
        // problem loading reconnection manager
    }
}

public void init() {
    gson = new Gson();
    mMessageListener = new MMessageListener();
    mChatManagerListener = new ChatManagerListenerImpl();
    initialiseConnection();
}

private void initialiseConnection() {

    cm =
            (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
    activeNetwork = cm.getActiveNetworkInfo();

    XMPPTCPConnectionConfiguration.Builder config = XMPPTCPConnectionConfiguration
            .builder();
    config.setSendPresence(true);
    config.setSecurityMode(XMPPTCPConnectionConfiguration.SecurityMode.required);
    config.setCompressionEnabled(true);
    config.setServiceName(GlobalVariables.service);
    config.setHost(serverAddress);
    config.setPort(5222);
    config.setDebuggerEnabled(true);

    try {
        SSLContext sc = SSLContext.getInstance("TLS");
        MemorizingTrustManager mtm = new MemorizingTrustManager(context);
        sc.init(null, new X509TrustManager[] { mtm }, new java.security.SecureRandom());
        config.setCustomSSLContext(sc);
        config.setHostnameVerifier(mtm.wrapHostnameVerifier(new org.apache.http.conn.ssl.StrictHostnameVerifier()));
    } catch (NoSuchAlgorithmException e) {
        throw new IllegalStateException(e);
    } catch (KeyManagementException e) {
        throw new IllegalStateException(e);
    }
    connection = new XMPPTCPConnection(config.build());
    XMPPConnectionListener connectionListener = new XMPPConnectionListener();
    connection.addConnectionListener(connectionListener);

    PingManager pingManager = PingManager.getInstanceFor(connection);
    pingManager.setPingInterval(900); // 15 min
    pingManager.registerPingFailedListener(new PingFailedListener(){
        @Override
        public void pingFailed() {
            // Do operation to handle if ping fail like force reconnect etc
            Log.e("PingManager","Ping Failed, reconnection");
            connected = false;
            chat_created = false;
            loggedin = false;
            //disconnect();
            connect("ping_manager");
        }

    });
    ServerPingWithAlarmManager.getInstanceFor(connection).isEnabled(); //для пинга во время глубокого сна
}

public void disconnect() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            connection.disconnect();
        }
    }).start();
}

public void connect(final String caller) {
    AsyncTask<Void, Void, Boolean> connectionThread = new AsyncTask<Void, Void, Boolean>() {
        @Override
        protected synchronized Boolean doInBackground (Void...arg0){
            if (connection.isConnected())
                return false;
            isconnecting = true;
            if (isToasted)
                new Handler(Looper.getMainLooper()).post(new Runnable() {

                    @Override
                    public void run() {

                        Toast.makeText(context, caller + "=>connecting....", Toast.LENGTH_LONG).show();
                    }
                });
            Log.e("Connect() Function", caller + "=&gt;connecting....");

            try {
                connection.setPacketReplyTimeout(20000);
                // Enable automatic reconnection
                connMgr = ReconnectionManager.getInstanceFor(connection);
                connMgr.enableAutomaticReconnection();
                connMgr.setFixedDelay(2);
                //connMgr.setDefaultFixedDelay(1);
                //connMgr.setReconnectionPolicy(ReconnectionManager.ReconnectionPolicy.FIXED_DELAY);
                connection.connect();
                DeliveryReceiptManager dm = DeliveryReceiptManager
                        .getInstanceFor(connection);
                dm.setAutoReceiptMode(AutoReceiptMode.always);
                dm.addReceiptReceivedListener(new ReceiptReceivedListener() {

                    @Override
                    public void onReceiptReceived(final String fromid,
                                                  final String toid, final String msgid,
                                                  final Stanza packet) {

                    }
                });
                connected = true;
                MainActivity.fConn = true;
            } catch (IOException e) {
                if (isToasted)
                    new Handler(Looper.getMainLooper())
                            .post(new Runnable() {

                                @Override
                                public void run() {

                                    Toast.makeText(
                                            context,
                                            "(" + caller + ")"
                                                    + "IOException: ",
                                            Toast.LENGTH_SHORT).show();
                                }
                            });

                Log.e("(" + caller + ")", "IOException: " + e.getMessage());
            } catch (SmackException e) {
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                      // Toast.makeText(context, "(" + caller + ")" + "SMACKException: ", Toast.LENGTH_SHORT).show();
                    }
                });
                Log.e("(" + caller + ")",
                        "SMACKException: " + e.getMessage());
                //mHandler.post(checkConn); //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!<-- Problem place
                //instance = null;
                //MyService.getInstance().destroyService(); 
            } catch (XMPPException e) {
                if (isToasted)

                    new Handler(Looper.getMainLooper())
                            .post(new Runnable() {

                                @Override
                                public void run() {

                                    Toast.makeText(
                                            context,
                                            "(" + caller + ")"
                                                    + "XMPPException: ",
                                            Toast.LENGTH_SHORT).show();
                                }
                            });
                Log.e("connect(" + caller + ")",
                        "XMPPException: " + e.getMessage());

            }
            return isconnecting = false;
        }
    } ;
    connectionThread.execute();
}

public static void login() {
    try {
        Log.e(loginUser,passwordUser);
        loginUser = GlobalVariables.user_id;
        passwordUser = GlobalVariables.user_password;
        connection.login(loginUser, passwordUser);
        Log.e("LOGIN", "Yey! We're connected to the Xmpp server!");
        sendBufMessages();
        myRoster = Roster.getInstanceFor(connection);
        if (!myRoster.isLoaded()) {
            try{ myRoster.reloadAndWait(); }
            catch (Exception e) {System.out.println(e);}
        }
        myRoster.setDefaultSubscriptionMode(Roster.SubscriptionMode.accept_all);
        myRoster.addRosterListener(new RosterListener() {
            public void entriesAdded(Collection<String> addresses) {}
            public void entriesDeleted(Collection<String> addresses) {}
            public void entriesUpdated(Collection<String> addresses) {}
            public void presenceChanged(Presence presence) {
                Log.e("Roster","Presence changed: " + presence.getFrom() + " " + presence);
                String uname = presence.getFrom();
                int pos = uname.indexOf('@',0);
                uname = uname.substring(0,pos);
                Presence.Type ptype = presence.getType(); //Presence.type.available
                for(int i=0;i<stat_list.size();i++){
                    HashMap<String,String> item = new HashMap<String, String>();
                    item = stat_list.get(i);
                    if (uname.equals(item.get("user_id").toString())) { stat_list.remove(i); break;}
                }
                HashMap<String,String> item = new HashMap<>();
                item.put("user_id",uname);
                if (ptype == Presence.Type.available){ item.put("onl","true"); }
                if (ptype == Presence.Type.unavailable){ item.put("onl","false"); }
                stat_list.add(0,item);
                if (MainActivity.chatlist_selected) { ChatList.getInstance().startStatProc(); }
                if (GlobalVariables.onchat == true & GlobalVariables.vuser_id.equals(uname)){
                    if (ptype == Presence.Type.available) { Chat.setUserStatus("onl"); }
                    if (ptype == Presence.Type.unavailable) {Chat.setUserStatus("offl");}
                }
            }
        });
    } catch (XMPPException | SmackException | IOException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private class ChatManagerListenerImpl implements ChatManagerListener {
    @Override
    public void chatCreated(final org.jivesoftware.smack.chat.Chat chat,
                            final boolean createdLocally) {
        if (!createdLocally)
            chat.addMessageListener(mMessageListener);

    }

}

public void sendMessage(ChatMessage chatMessage) {
.....
}

public class XMPPConnectionListener implements ConnectionListener {
    @Override
    public void connected(final XMPPConnection connection) {

        Log.e("xmpp", "Connected!");
        connected = true;
        curr_delay = 0; connMgr.setFixedDelay(2);
            login();
    }

    @Override
    public void connectionClosed() {
        if (isToasted)

            new Handler(Looper.getMainLooper()).post(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub

                    Toast.makeText(context, "ConnectionCLosed!",
                            Toast.LENGTH_SHORT).show();

                }
            });
        Log.d("xmpp", "ConnectionCLosed!");
        connected = false;
        chat_created = false;
        loggedin = false;
    }

    @Override
    public void connectionClosedOnError(Exception arg0) {
        if (isToasted)
            new Handler(Looper.getMainLooper()).post(new Runnable() {

                @Override
                public void run() {
                    Toast.makeText(context, "ConnectionClosedOn Error!!",
                            Toast.LENGTH_SHORT).show();

                }
            });
        Log.e("xmpp", "ConnectionClosedOn Error!");
        connected = false;
        chat_created = false;
        loggedin = false;
    }

    @Override
    public void reconnectingIn(int arg0) {

        Log.e("xmpp", "Reconnectingin " + arg0);
        if (arg0==0 & curr_delay<5){
            curr_delay++;
            connMgr.setFixedDelay(rt_arr[curr_delay]);
        }
        loggedin = false;
    }

    @Override
    public void reconnectionFailed(Exception arg0) {
        if (isToasted)

            new Handler(Looper.getMainLooper()).post(new Runnable() {

                @Override
                public void run() {

                    Toast.makeText(context, "ReconnectionFailed!",
                            Toast.LENGTH_SHORT).show();

                }
            });
        Log.d("xmpp", "ReconnectionFailed!");
        connected = false;

        chat_created = false;
        loggedin = false;
    }

    @Override
    public void reconnectionSuccessful() {
        if (isToasted)

            new Handler(Looper.getMainLooper()).post(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub

                    Toast.makeText(context, "REConnected!",
                            Toast.LENGTH_SHORT).show();

                }
            });
        Log.d("xmpp", "ReconnectionSuccessful");
        curr_delay = 0; connMgr.setFixedDelay(2);
        connected = true;
        //MainActivity.fConn = true;
        chat_created = false;
        loggedin = false;
    }

    @Override
    public void authenticated(XMPPConnection arg0, boolean arg1) {
        Log.d("xmpp", "Authenticated!");
        loggedin = true;

        ChatManager.getInstanceFor(connection).addChatListener(
                mChatManagerListener);

        chat_created = false;
        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }).start();
        if (isToasted)

            new Handler(Looper.getMainLooper()).post(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub

                    Toast.makeText(context, "Connected!",Toast.LENGTH_SHORT).show();

                }
            });
    }
}

private class MMessageListener implements ChatMessageListener {

    @Override
    public void processMessage(final org.jivesoftware.smack.chat.Chat chat,
                               final Message message) {
        Log.i("MyXMPP_MESSAGE_LISTENER", "Xmpp message received: '"
                + message);

...
        }
    }



private Runnable checkConn = new Runnable() {
    public void run() {
        try {
            Socket socket = new Socket(host, 5222);
            socket.shutdownInput();
            socket.shutdownOutput();
            socket.close();
        } catch(Exception e){Log.e("Socket",e.toString());}
        disconnect();
        Log.e("MyXMPP","disconnect");
        mHandler.postDelayed(checkConn2,3000);
    }
};
private Runnable checkConn2 = new Runnable() {
    public void run() {
        connect("After SmackException");
    }
};


}

Update 26.09.17. After several days of experiments I still not find a solution. I tried to use foreground service, PingManager, broadcastreceiver to recreate service, but no results. Now I try to use pressure signals to the application when low memory in system. Update 05.10.17 I still not find a solution. I tested this programm on Android 4.1.2. It works fine. On Android 5.1.1 it works about 3 minutes after exiting the activity and then I receive connection refused error. When I return to activity, errors disappears. On Android 6.0.1 similar situation, but the error is slightly different java.net.SocketTimeoutException: failed to connect to /94.130.25.242 (port 80) after 10000ms. I think that the system blocks network activity in services after a while, but never in activities (?).

2
I just saw, that this error not only in smack, simple http requests calls the same error after running heavy apps. java.net.ConnectException: failed to connect to /94.x.x.x (port 80) after 15000ms: isConnected failed: ECONNREFUSED (Connection refused)xdamir79

2 Answers

0
votes

It is impossible to use long network connections in background on Xiaomi phones. It's MIUI blocks any network connections after some time. For critical network connections, you can use Firebase Cloud Messaging, which have high priority in Android system. It can initiate necessary background job. Also it can notify user about incoming message. After resceiving this message, user will click to it and will return to his activity, and xmpp connection will be restored.

0
votes

Try to add permissions in your AndroidManifest.xml

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND"/>
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
    <uses-permission android:name="android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND"/>

And code below to onCreate() in your service. I hope it help someone.

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
            if (pm != null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    Intent intent = new Intent();
                    String packageName = getPackageName();
                    if (!pm.isIgnoringBatteryOptimizations(packageName)) {
                        intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                        intent.setData(Uri.parse("package:" + packageName));
                        startActivity(intent);
                    }
                }

                Log.d(TAG, "PowerManager.SCREEN_DIM_WAKE_LOCK+PowerManager.ON_AFTER_RELEASE");
                pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK + PowerManager.ON_AFTER_RELEASE, "wetwars:activityrecognition").acquire();
                pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "WetWars:WakeLockActivity").acquire();
            }