AccountStateProcessor.java

  1package im.conversations.android.xmpp.processor;
  2
  3import static eu.siacs.conversations.utils.Random.SECURE_RANDOM;
  4
  5import android.util.Log;
  6import eu.siacs.conversations.Config;
  7import eu.siacs.conversations.entities.Account;
  8import eu.siacs.conversations.entities.Conversation;
  9import eu.siacs.conversations.http.ServiceOutageStatus;
 10import eu.siacs.conversations.services.XmppConnectionService;
 11import eu.siacs.conversations.xmpp.XmppConnection;
 12import java.util.ArrayList;
 13import java.util.List;
 14import java.util.concurrent.TimeUnit;
 15import java.util.function.Consumer;
 16
 17public class AccountStateProcessor extends XmppConnection.Delegate
 18        implements Consumer<Account.State> {
 19
 20    private final XmppConnectionService service;
 21
 22    public AccountStateProcessor(final XmppConnectionService service, XmppConnection connection) {
 23        super(service.getApplicationContext(), connection);
 24        this.service = service;
 25    }
 26
 27    @Override
 28    public void accept(final Account.State status) {
 29        final var account = getAccount();
 30        if (ServiceOutageStatus.isPossibleOutage(status)) {
 31            this.service.fetchServiceOutageStatus(account);
 32        }
 33        this.service.updateAccountUi();
 34
 35        if (account.getStatus() == Account.State.ONLINE || account.getStatus().isError()) {
 36            this.service.getQuickConversationsService().signalAccountStateChange();
 37        }
 38
 39        if (account.getStatus() == Account.State.ONLINE) {
 40            synchronized (this.service.mLowPingTimeoutMode) {
 41                if (this.service.mLowPingTimeoutMode.remove(account.getJid().asBareJid())) {
 42                    Log.d(
 43                            Config.LOGTAG,
 44                            account.getJid().asBareJid() + ": leaving low ping timeout mode");
 45                }
 46            }
 47            if (account.setShowErrorNotification(true)) {
 48                this.service.databaseBackend.updateAccount(account);
 49            }
 50            this.service.getMessageArchiveService().executePendingQueries(account);
 51            if (connection != null && connection.getFeatures().csi()) {
 52                if (this.service.checkListeners()) {
 53                    Log.d(Config.LOGTAG, account.getJid().asBareJid() + " sending csi//inactive");
 54                    connection.sendInactive();
 55                } else {
 56                    Log.d(Config.LOGTAG, account.getJid().asBareJid() + " sending csi//active");
 57                    connection.sendActive();
 58                }
 59            }
 60            List<Conversation> conversations = this.service.getConversations();
 61            for (Conversation conversation : conversations) {
 62                final boolean inProgressJoin;
 63                synchronized (account.inProgressConferenceJoins) {
 64                    inProgressJoin = account.inProgressConferenceJoins.contains(conversation);
 65                }
 66                final boolean pendingJoin;
 67                synchronized (account.pendingConferenceJoins) {
 68                    pendingJoin = account.pendingConferenceJoins.contains(conversation);
 69                }
 70                if (conversation.getAccount() == account && !pendingJoin && !inProgressJoin) {
 71                    this.service.sendUnsentMessages(conversation);
 72                }
 73            }
 74            final List<Conversation> pendingLeaves;
 75            synchronized (account.pendingConferenceLeaves) {
 76                pendingLeaves = new ArrayList<>(account.pendingConferenceLeaves);
 77                account.pendingConferenceLeaves.clear();
 78            }
 79            for (Conversation conversation : pendingLeaves) {
 80                this.service.leaveMuc(conversation);
 81            }
 82            final List<Conversation> pendingJoins;
 83            synchronized (account.pendingConferenceJoins) {
 84                pendingJoins = new ArrayList<>(account.pendingConferenceJoins);
 85                account.pendingConferenceJoins.clear();
 86            }
 87            for (Conversation conversation : pendingJoins) {
 88                this.service.joinMuc(conversation);
 89            }
 90            this.service.scheduleWakeUpCall(
 91                    Config.PING_MAX_INTERVAL * 1000L, account.getUuid().hashCode());
 92        } else if (account.getStatus() == Account.State.OFFLINE
 93                || account.getStatus() == Account.State.DISABLED
 94                || account.getStatus() == Account.State.LOGGED_OUT) {
 95            this.service.resetSendingToWaiting(account);
 96            if (account.isConnectionEnabled() && this.service.isInLowPingTimeoutMode(account)) {
 97                Log.d(
 98                        Config.LOGTAG,
 99                        account.getJid().asBareJid()
100                                + ": went into offline state during low ping mode."
101                                + " reconnecting now");
102                this.service.reconnectAccount(account, true, false);
103            } else {
104                final int timeToReconnect = SECURE_RANDOM.nextInt(10) + 2;
105                this.service.scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode());
106            }
107        } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) {
108            this.service.databaseBackend.updateAccount(account);
109            this.service.reconnectAccount(account, true, false);
110        } else if (account.getStatus() != Account.State.CONNECTING
111                && account.getStatus() != Account.State.NO_INTERNET) {
112            this.service.resetSendingToWaiting(account);
113            if (connection != null && account.getStatus().isAttemptReconnect()) {
114                final boolean aggressive =
115                        account.getStatus() == Account.State.SEE_OTHER_HOST
116                                || this.service.hasJingleRtpConnection(account);
117                final int next = connection.getTimeToNextAttempt(aggressive);
118                final boolean lowPingTimeoutMode = this.service.isInLowPingTimeoutMode(account);
119                if (next <= 0) {
120                    Log.d(
121                            Config.LOGTAG,
122                            account.getJid().asBareJid()
123                                    + ": error connecting account. reconnecting now."
124                                    + " lowPingTimeout="
125                                    + lowPingTimeoutMode);
126                    this.service.reconnectAccount(account, true, false);
127                } else {
128                    final int attempt = connection.getAttempt() + 1;
129                    Log.d(
130                            Config.LOGTAG,
131                            account.getJid().asBareJid()
132                                    + ": error connecting account. try again in "
133                                    + next
134                                    + "s for the "
135                                    + attempt
136                                    + " time. lowPingTimeout="
137                                    + lowPingTimeoutMode
138                                    + ", aggressive="
139                                    + aggressive);
140                    this.service.scheduleWakeUpCall(next, account.getUuid().hashCode());
141                    if (aggressive) {
142                        this.service.internalPingExecutor.schedule(
143                                service::manageAccountConnectionStatesInternal,
144                                (next * 1000L) + 50,
145                                TimeUnit.MILLISECONDS);
146                    }
147                }
148            }
149        }
150        this.service.getNotificationService().updateErrorNotification();
151    }
152}