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