@@ -45,55 +45,18 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
-
import androidx.annotation.BoolRes;
import androidx.annotation.IntegerRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.RemoteInput;
import androidx.core.content.ContextCompat;
-
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-
-import org.conscrypt.Conscrypt;
-import org.jxmpp.stringprep.libidn.LibIdnXmppStringprep;
-import org.openintents.openpgp.IOpenPgpService2;
-import org.openintents.openpgp.util.OpenPgpApi;
-import org.openintents.openpgp.util.OpenPgpServiceConnection;
-
-import java.io.File;
-import java.security.Security;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.WeakHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Consumer;
-
import eu.siacs.conversations.AppSettings;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@@ -175,7 +138,39 @@ import eu.siacs.conversations.xmpp.mam.MamReference;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.pep.PublishOptions;
import im.conversations.android.xmpp.model.stanza.Iq;
+import java.io.File;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
import me.leolin.shortcutbadger.ShortcutBadger;
+import org.conscrypt.Conscrypt;
+import org.jxmpp.stringprep.libidn.LibIdnXmppStringprep;
+import org.openintents.openpgp.IOpenPgpService2;
+import org.openintents.openpgp.util.OpenPgpApi;
+import org.openintents.openpgp.util.OpenPgpServiceConnection;
public class XmppConnectionService extends Service {
@@ -183,7 +178,8 @@ public class XmppConnectionService extends Service {
public static final String ACTION_MARK_AS_READ = "mark_as_read";
public static final String ACTION_SNOOZE = "snooze";
public static final String ACTION_CLEAR_MESSAGE_NOTIFICATION = "clear_message_notification";
- public static final String ACTION_CLEAR_MISSED_CALL_NOTIFICATION = "clear_missed_call_notification";
+ public static final String ACTION_CLEAR_MISSED_CALL_NOTIFICATION =
+ "clear_missed_call_notification";
public static final String ACTION_DISMISS_ERROR_NOTIFICATIONS = "dismiss_error";
public static final String ACTION_TRY_AGAIN = "try_again";
@@ -196,22 +192,30 @@ public class XmppConnectionService extends Service {
public static final String ACTION_DISMISS_CALL = "dismiss_call";
public static final String ACTION_END_CALL = "end_call";
public static final String ACTION_PROVISION_ACCOUNT = "provision_account";
- public static final String ACTION_CALL_INTEGRATION_SERVICE_STARTED = "call_integration_service_started";
- private static final String ACTION_POST_CONNECTIVITY_CHANGE = "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE";
- public static final String ACTION_RENEW_UNIFIED_PUSH_ENDPOINTS = "eu.siacs.conversations.UNIFIED_PUSH_RENEW";
+ public static final String ACTION_CALL_INTEGRATION_SERVICE_STARTED =
+ "call_integration_service_started";
+ private static final String ACTION_POST_CONNECTIVITY_CHANGE =
+ "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE";
+ public static final String ACTION_RENEW_UNIFIED_PUSH_ENDPOINTS =
+ "eu.siacs.conversations.UNIFIED_PUSH_RENEW";
public static final String ACTION_QUICK_LOG = "eu.siacs.conversations.QUICK_LOG";
private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp";
public final CountDownLatch restoredFromDatabaseLatch = new CountDownLatch(1);
- private final static Executor FILE_OBSERVER_EXECUTOR = Executors.newSingleThreadExecutor();
- public final static Executor FILE_ATTACHMENT_EXECUTOR = Executors.newSingleThreadExecutor();
-
- private final ScheduledExecutorService internalPingExecutor = Executors.newSingleThreadScheduledExecutor();
- private final static SerialSingleThreadExecutor VIDEO_COMPRESSION_EXECUTOR = new SerialSingleThreadExecutor("VideoCompression");
- private final SerialSingleThreadExecutor mDatabaseWriterExecutor = new SerialSingleThreadExecutor("DatabaseWriter");
- private final SerialSingleThreadExecutor mDatabaseReaderExecutor = new SerialSingleThreadExecutor("DatabaseReader");
- private final SerialSingleThreadExecutor mNotificationExecutor = new SerialSingleThreadExecutor("NotificationExecutor");
+ private static final Executor FILE_OBSERVER_EXECUTOR = Executors.newSingleThreadExecutor();
+ public static final Executor FILE_ATTACHMENT_EXECUTOR = Executors.newSingleThreadExecutor();
+
+ private final ScheduledExecutorService internalPingExecutor =
+ Executors.newSingleThreadScheduledExecutor();
+ private static final SerialSingleThreadExecutor VIDEO_COMPRESSION_EXECUTOR =
+ new SerialSingleThreadExecutor("VideoCompression");
+ private final SerialSingleThreadExecutor mDatabaseWriterExecutor =
+ new SerialSingleThreadExecutor("DatabaseWriter");
+ private final SerialSingleThreadExecutor mDatabaseReaderExecutor =
+ new SerialSingleThreadExecutor("DatabaseReader");
+ private final SerialSingleThreadExecutor mNotificationExecutor =
+ new SerialSingleThreadExecutor("NotificationExecutor");
private final ReplacingTaskManager mRosterSyncTaskManager = new ReplacingTaskManager();
private final IBinder mBinder = new XmppConnectionBinder();
private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
@@ -219,17 +223,19 @@ public class XmppConnectionService extends Service {
private final Set<String> mInProgressAvatarFetches = new HashSet<>();
private final Set<String> mOmittedPepAvatarFetches = new HashSet<>();
private final HashSet<Jid> mLowPingTimeoutMode = new HashSet<>();
- private final Consumer<Iq> mDefaultIqHandler = (packet) -> {
- if (packet.getType() != Iq.Type.RESULT) {
- final var error = packet.getError();
- String text = error != null ? error.findChildContent("text") : null;
- if (text != null) {
- Log.d(Config.LOGTAG, "received iq error: " + text);
- }
- }
- };
+ private final Consumer<Iq> mDefaultIqHandler =
+ (packet) -> {
+ if (packet.getType() != Iq.Type.RESULT) {
+ final var error = packet.getError();
+ String text = error != null ? error.findChildContent("text") : null;
+ if (text != null) {
+ Log.d(Config.LOGTAG, "received iq error: " + text);
+ }
+ }
+ };
public DatabaseBackend databaseBackend;
- private final ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor("ContactMerger");
+ private final ReplacingSerialSingleThreadExecutor mContactMergerExecutor =
+ new ReplacingSerialSingleThreadExecutor("ContactMerger");
private long mLastActivity = 0;
private final AppSettings appSettings = new AppSettings(this);
@@ -237,204 +243,253 @@ public class XmppConnectionService extends Service {
private MemorizingTrustManager mMemorizingTrustManager;
private final NotificationService mNotificationService = new NotificationService(this);
private final UnifiedPushBroker unifiedPushBroker = new UnifiedPushBroker(this);
- private final ChannelDiscoveryService mChannelDiscoveryService = new ChannelDiscoveryService(this);
+ private final ChannelDiscoveryService mChannelDiscoveryService =
+ new ChannelDiscoveryService(this);
private final ShortcutService mShortcutService = new ShortcutService(this);
private final AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false);
private final AtomicBoolean mOngoingVideoTranscoding = new AtomicBoolean(false);
private final AtomicBoolean mForceDuringOnCreate = new AtomicBoolean(false);
private final AtomicReference<OngoingCall> ongoingCall = new AtomicReference<>();
private final MessageGenerator mMessageGenerator = new MessageGenerator(this);
- public OnContactStatusChanged onContactStatusChanged = (contact, online) -> {
- Conversation conversation = find(getConversations(), contact);
- if (conversation != null) {
- if (online) {
- if (contact.getPresences().size() == 1) {
- sendUnsentMessages(conversation);
+ public OnContactStatusChanged onContactStatusChanged =
+ (contact, online) -> {
+ Conversation conversation = find(getConversations(), contact);
+ if (conversation != null) {
+ if (online) {
+ if (contact.getPresences().size() == 1) {
+ sendUnsentMessages(conversation);
+ }
+ }
}
- }
- }
- };
+ };
private final PresenceGenerator mPresenceGenerator = new PresenceGenerator(this);
private List<Account> accounts;
- private final JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(this);
+ private final JingleConnectionManager mJingleConnectionManager =
+ new JingleConnectionManager(this);
private final HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(this);
private final AvatarService mAvatarService = new AvatarService(this);
private final MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
private final PushManagementService mPushManagementService = new PushManagementService(this);
- private final QuickConversationsService mQuickConversationsService = new QuickConversationsService(this);
- private final ConversationsFileObserver fileObserver = new ConversationsFileObserver(
- Environment.getExternalStorageDirectory().getAbsolutePath()
- ) {
- @Override
- public void onEvent(final int event, final File file) {
- markFileDeleted(file);
- }
- };
- private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
-
- @Override
- public boolean onMessageAcknowledged(final Account account, final Jid to, final String id) {
- if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX)) {
- final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX.length());
- mJingleConnectionManager.updateProposedSessionDiscovered(
- account,
- to,
- sessionId,
- JingleConnectionManager.DeviceDiscoveryState.SEARCHING_ACKNOWLEDGED
- );
- }
-
+ private final QuickConversationsService mQuickConversationsService =
+ new QuickConversationsService(this);
+ private final ConversationsFileObserver fileObserver =
+ new ConversationsFileObserver(
+ Environment.getExternalStorageDirectory().getAbsolutePath()) {
+ @Override
+ public void onEvent(final int event, final File file) {
+ markFileDeleted(file);
+ }
+ };
+ private final OnMessageAcknowledged mOnMessageAcknowledgedListener =
+ new OnMessageAcknowledged() {
- final Jid bare = to.asBareJid();
+ @Override
+ public boolean onMessageAcknowledged(
+ final Account account, final Jid to, final String id) {
+ if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX)) {
+ final String sessionId =
+ id.substring(
+ JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX
+ .length());
+ mJingleConnectionManager.updateProposedSessionDiscovered(
+ account,
+ to,
+ sessionId,
+ JingleConnectionManager.DeviceDiscoveryState
+ .SEARCHING_ACKNOWLEDGED);
+ }
- for (final Conversation conversation : getConversations()) {
- if (conversation.getAccount() == account && conversation.getJid().asBareJid().equals(bare)) {
- final Message message = conversation.findUnsentMessageWithUuid(id);
- if (message != null) {
- message.setStatus(Message.STATUS_SEND);
- message.setErrorMessage(null);
- databaseBackend.updateMessage(message, false);
- return true;
+ final Jid bare = to.asBareJid();
+
+ for (final Conversation conversation : getConversations()) {
+ if (conversation.getAccount() == account
+ && conversation.getJid().asBareJid().equals(bare)) {
+ final Message message = conversation.findUnsentMessageWithUuid(id);
+ if (message != null) {
+ message.setStatus(Message.STATUS_SEND);
+ message.setErrorMessage(null);
+ databaseBackend.updateMessage(message, false);
+ return true;
+ }
+ }
}
+ return false;
}
- }
- return false;
- }
- };
+ };
private boolean destroyed = false;
private int unreadCount = -1;
- //Ui callback listeners
- private final Set<OnConversationUpdate> mOnConversationUpdates = Collections.newSetFromMap(new WeakHashMap<OnConversationUpdate, Boolean>());
- private final Set<OnShowErrorToast> mOnShowErrorToasts = Collections.newSetFromMap(new WeakHashMap<OnShowErrorToast, Boolean>());
- private final Set<OnAccountUpdate> mOnAccountUpdates = Collections.newSetFromMap(new WeakHashMap<OnAccountUpdate, Boolean>());
- private final Set<OnCaptchaRequested> mOnCaptchaRequested = Collections.newSetFromMap(new WeakHashMap<OnCaptchaRequested, Boolean>());
- private final Set<OnRosterUpdate> mOnRosterUpdates = Collections.newSetFromMap(new WeakHashMap<OnRosterUpdate, Boolean>());
- private final Set<OnUpdateBlocklist> mOnUpdateBlocklist = Collections.newSetFromMap(new WeakHashMap<OnUpdateBlocklist, Boolean>());
- private final Set<OnMucRosterUpdate> mOnMucRosterUpdate = Collections.newSetFromMap(new WeakHashMap<OnMucRosterUpdate, Boolean>());
- private final Set<OnKeyStatusUpdated> mOnKeyStatusUpdated = Collections.newSetFromMap(new WeakHashMap<OnKeyStatusUpdated, Boolean>());
- private final Set<OnJingleRtpConnectionUpdate> onJingleRtpConnectionUpdate = Collections.newSetFromMap(new WeakHashMap<OnJingleRtpConnectionUpdate, Boolean>());
+ // Ui callback listeners
+ private final Set<OnConversationUpdate> mOnConversationUpdates =
+ Collections.newSetFromMap(new WeakHashMap<OnConversationUpdate, Boolean>());
+ private final Set<OnShowErrorToast> mOnShowErrorToasts =
+ Collections.newSetFromMap(new WeakHashMap<OnShowErrorToast, Boolean>());
+ private final Set<OnAccountUpdate> mOnAccountUpdates =
+ Collections.newSetFromMap(new WeakHashMap<OnAccountUpdate, Boolean>());
+ private final Set<OnCaptchaRequested> mOnCaptchaRequested =
+ Collections.newSetFromMap(new WeakHashMap<OnCaptchaRequested, Boolean>());
+ private final Set<OnRosterUpdate> mOnRosterUpdates =
+ Collections.newSetFromMap(new WeakHashMap<OnRosterUpdate, Boolean>());
+ private final Set<OnUpdateBlocklist> mOnUpdateBlocklist =
+ Collections.newSetFromMap(new WeakHashMap<OnUpdateBlocklist, Boolean>());
+ private final Set<OnMucRosterUpdate> mOnMucRosterUpdate =
+ Collections.newSetFromMap(new WeakHashMap<OnMucRosterUpdate, Boolean>());
+ private final Set<OnKeyStatusUpdated> mOnKeyStatusUpdated =
+ Collections.newSetFromMap(new WeakHashMap<OnKeyStatusUpdated, Boolean>());
+ private final Set<OnJingleRtpConnectionUpdate> onJingleRtpConnectionUpdate =
+ Collections.newSetFromMap(new WeakHashMap<OnJingleRtpConnectionUpdate, Boolean>());
private final Object LISTENER_LOCK = new Object();
-
public final Set<String> FILENAMES_TO_IGNORE_DELETION = new HashSet<>();
-
-
private final AtomicLong mLastExpiryRun = new AtomicLong(0);
- private final LruCache<Pair<String, String>, ServiceDiscoveryResult> discoCache = new LruCache<>(20);
- private final OnStatusChanged statusListener = new OnStatusChanged() {
-
- @Override
- public void onStatusChanged(final Account account) {
- XmppConnection connection = account.getXmppConnection();
- updateAccountUi();
+ private final LruCache<Pair<String, String>, ServiceDiscoveryResult> discoCache =
+ new LruCache<>(20);
+ private final OnStatusChanged statusListener =
+ new OnStatusChanged() {
- if (account.getStatus() == Account.State.ONLINE || account.getStatus().isError()) {
- mQuickConversationsService.signalAccountStateChange();
- }
+ @Override
+ public void onStatusChanged(final Account account) {
+ XmppConnection connection = account.getXmppConnection();
+ updateAccountUi();
- if (account.getStatus() == Account.State.ONLINE) {
- synchronized (mLowPingTimeoutMode) {
- if (mLowPingTimeoutMode.remove(account.getJid().asBareJid())) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": leaving low ping timeout mode");
+ if (account.getStatus() == Account.State.ONLINE
+ || account.getStatus().isError()) {
+ mQuickConversationsService.signalAccountStateChange();
}
- }
- if (account.setShowErrorNotification(true)) {
- databaseBackend.updateAccount(account);
- }
- mMessageArchiveService.executePendingQueries(account);
- if (connection != null && connection.getFeatures().csi()) {
- if (checkListeners()) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + " sending csi//inactive");
- connection.sendInactive();
- } else {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + " sending csi//active");
- connection.sendActive();
- }
- }
- List<Conversation> conversations = getConversations();
- for (Conversation conversation : conversations) {
- final boolean inProgressJoin;
- synchronized (account.inProgressConferenceJoins) {
- inProgressJoin = account.inProgressConferenceJoins.contains(conversation);
- }
- final boolean pendingJoin;
- synchronized (account.pendingConferenceJoins) {
- pendingJoin = account.pendingConferenceJoins.contains(conversation);
- }
- if (conversation.getAccount() == account
- && !pendingJoin
- && !inProgressJoin) {
- sendUnsentMessages(conversation);
- }
- }
- final List<Conversation> pendingLeaves;
- synchronized (account.pendingConferenceLeaves) {
- pendingLeaves = new ArrayList<>(account.pendingConferenceLeaves);
- account.pendingConferenceLeaves.clear();
- }
- for (Conversation conversation : pendingLeaves) {
- leaveMuc(conversation);
- }
- final List<Conversation> pendingJoins;
- synchronized (account.pendingConferenceJoins) {
- pendingJoins = new ArrayList<>(account.pendingConferenceJoins);
- account.pendingConferenceJoins.clear();
- }
- for (Conversation conversation : pendingJoins) {
- joinMuc(conversation);
- }
- scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
- } else if (account.getStatus() == Account.State.OFFLINE || account.getStatus() == Account.State.DISABLED || account.getStatus() == Account.State.LOGGED_OUT) {
- resetSendingToWaiting(account);
- if (account.isConnectionEnabled() && isInLowPingTimeoutMode(account)) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": went into offline state during low ping mode. reconnecting now");
- reconnectAccount(account, true, false);
- } else {
- final int timeToReconnect = SECURE_RANDOM.nextInt(10) + 2;
- scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode());
- }
- } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) {
- databaseBackend.updateAccount(account);
- reconnectAccount(account, true, false);
- } else if (account.getStatus() != Account.State.CONNECTING && account.getStatus() != Account.State.NO_INTERNET) {
- resetSendingToWaiting(account);
- if (connection != null && account.getStatus().isAttemptReconnect()) {
- final boolean aggressive = account.getStatus() == Account.State.SEE_OTHER_HOST
- || hasJingleRtpConnection(account);
- final int next = connection.getTimeToNextAttempt(aggressive);
- final boolean lowPingTimeoutMode = isInLowPingTimeoutMode(account);
- if (next <= 0) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error connecting account. reconnecting now. lowPingTimeout=" + lowPingTimeoutMode);
+ if (account.getStatus() == Account.State.ONLINE) {
+ synchronized (mLowPingTimeoutMode) {
+ if (mLowPingTimeoutMode.remove(account.getJid().asBareJid())) {
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": leaving low ping timeout mode");
+ }
+ }
+ if (account.setShowErrorNotification(true)) {
+ databaseBackend.updateAccount(account);
+ }
+ mMessageArchiveService.executePendingQueries(account);
+ if (connection != null && connection.getFeatures().csi()) {
+ if (checkListeners()) {
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid() + " sending csi//inactive");
+ connection.sendInactive();
+ } else {
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid() + " sending csi//active");
+ connection.sendActive();
+ }
+ }
+ List<Conversation> conversations = getConversations();
+ for (Conversation conversation : conversations) {
+ final boolean inProgressJoin;
+ synchronized (account.inProgressConferenceJoins) {
+ inProgressJoin =
+ account.inProgressConferenceJoins.contains(conversation);
+ }
+ final boolean pendingJoin;
+ synchronized (account.pendingConferenceJoins) {
+ pendingJoin = account.pendingConferenceJoins.contains(conversation);
+ }
+ if (conversation.getAccount() == account
+ && !pendingJoin
+ && !inProgressJoin) {
+ sendUnsentMessages(conversation);
+ }
+ }
+ final List<Conversation> pendingLeaves;
+ synchronized (account.pendingConferenceLeaves) {
+ pendingLeaves = new ArrayList<>(account.pendingConferenceLeaves);
+ account.pendingConferenceLeaves.clear();
+ }
+ for (Conversation conversation : pendingLeaves) {
+ leaveMuc(conversation);
+ }
+ final List<Conversation> pendingJoins;
+ synchronized (account.pendingConferenceJoins) {
+ pendingJoins = new ArrayList<>(account.pendingConferenceJoins);
+ account.pendingConferenceJoins.clear();
+ }
+ for (Conversation conversation : pendingJoins) {
+ joinMuc(conversation);
+ }
+ scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
+ } else if (account.getStatus() == Account.State.OFFLINE
+ || account.getStatus() == Account.State.DISABLED
+ || account.getStatus() == Account.State.LOGGED_OUT) {
+ resetSendingToWaiting(account);
+ if (account.isConnectionEnabled() && isInLowPingTimeoutMode(account)) {
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": went into offline state during low ping mode."
+ + " reconnecting now");
+ reconnectAccount(account, true, false);
+ } else {
+ final int timeToReconnect = SECURE_RANDOM.nextInt(10) + 2;
+ scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode());
+ }
+ } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) {
+ databaseBackend.updateAccount(account);
reconnectAccount(account, true, false);
- } else {
- final int attempt = connection.getAttempt() + 1;
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error connecting account. try again in " + next + "s for the " + attempt + " time. lowPingTimeout=" + lowPingTimeoutMode+", aggressive="+aggressive);
- scheduleWakeUpCall(next, account.getUuid().hashCode());
- if (aggressive) {
- internalPingExecutor.schedule(
- XmppConnectionService.this::manageAccountConnectionStatesInternal,
- (next * 1000L) + 50,
- TimeUnit.MILLISECONDS
- );
+ } else if (account.getStatus() != Account.State.CONNECTING
+ && account.getStatus() != Account.State.NO_INTERNET) {
+ resetSendingToWaiting(account);
+ if (connection != null && account.getStatus().isAttemptReconnect()) {
+ final boolean aggressive =
+ account.getStatus() == Account.State.SEE_OTHER_HOST
+ || hasJingleRtpConnection(account);
+ final int next = connection.getTimeToNextAttempt(aggressive);
+ final boolean lowPingTimeoutMode = isInLowPingTimeoutMode(account);
+ if (next <= 0) {
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": error connecting account. reconnecting now."
+ + " lowPingTimeout="
+ + lowPingTimeoutMode);
+ reconnectAccount(account, true, false);
+ } else {
+ final int attempt = connection.getAttempt() + 1;
+ Log.d(
+ Config.LOGTAG,
+ account.getJid().asBareJid()
+ + ": error connecting account. try again in "
+ + next
+ + "s for the "
+ + attempt
+ + " time. lowPingTimeout="
+ + lowPingTimeoutMode
+ + ", aggressive="
+ + aggressive);
+ scheduleWakeUpCall(next, account.getUuid().hashCode());
+ if (aggressive) {
+ internalPingExecutor.schedule(
+ XmppConnectionService.this
+ ::manageAccountConnectionStatesInternal,
+ (next * 1000L) + 50,
+ TimeUnit.MILLISECONDS);
+ }
+ }
}
}
+ getNotificationService().updateErrorNotification();
}
- }
- getNotificationService().updateErrorNotification();
- }
- };
+ };
private OpenPgpServiceConnection pgpServiceConnection;
private PgpEngine mPgpEngine = null;
private WakeLock wakeLock;
private LruCache<String, Bitmap> mBitmapCache;
private final BroadcastReceiver mInternalEventReceiver = new InternalEventReceiver();
- private final BroadcastReceiver mInternalRestrictedEventReceiver = new RestrictedEventReceiver(Arrays.asList(TorServiceUtils.ACTION_STATUS));
+ private final BroadcastReceiver mInternalRestrictedEventReceiver =
+ new RestrictedEventReceiver(Arrays.asList(TorServiceUtils.ACTION_STATUS));
private final BroadcastReceiver mInternalScreenEventReceiver = new InternalEventReceiver();
private static String generateFetchKey(Account account, final Avatar avatar) {
@@ -466,15 +521,16 @@ public class XmppConnectionService extends Service {
return null;
} else if (pgpServiceConnection != null && pgpServiceConnection.isBound()) {
if (this.mPgpEngine == null) {
- this.mPgpEngine = new PgpEngine(new OpenPgpApi(
- getApplicationContext(),
- pgpServiceConnection.getService()), this);
+ this.mPgpEngine =
+ new PgpEngine(
+ new OpenPgpApi(
+ getApplicationContext(), pgpServiceConnection.getService()),
+ this);
}
return mPgpEngine;
} else {
return null;
}
-
}
public OpenPgpApi getOpenPgpApi() {
@@ -499,7 +555,8 @@ public class XmppConnectionService extends Service {
return this.mAvatarService;
}
- public void attachLocationToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
+ public void attachLocationToConversation(
+ final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
int encryption = conversation.getNextEncryption();
if (encryption == Message.ENCRYPTION_PGP) {
encryption = Message.ENCRYPTION_DECRYPTED;
@@ -514,7 +571,11 @@ public class XmppConnectionService extends Service {
}
}
- public void attachFileToConversation(final Conversation conversation, final Uri uri, final String type, final UiCallback<Message> callback) {
+ public void attachFileToConversation(
+ final Conversation conversation,
+ final Uri uri,
+ final String type,
+ final UiCallback<Message> callback) {
final Message message;
if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
@@ -527,7 +588,8 @@ public class XmppConnectionService extends Service {
}
Log.d(Config.LOGTAG, "attachFile: type=" + message.getType());
Log.d(Config.LOGTAG, "counterpart=" + message.getCounterpart());
- final AttachFileToConversationRunnable runnable = new AttachFileToConversationRunnable(this, uri, type, message, callback);
+ final AttachFileToConversationRunnable runnable =
+ new AttachFileToConversationRunnable(this, uri, type, message, callback);
if (runnable.isVideoMessage()) {
VIDEO_COMPRESSION_EXECUTOR.execute(runnable);
} else {
@@ -535,7 +597,11 @@ public class XmppConnectionService extends Service {
}
}
- public void attachImageToConversation(final Conversation conversation, final Uri uri, final String type, final UiCallback<Message> callback) {
+ public void attachImageToConversation(
+ final Conversation conversation,
+ final Uri uri,
+ final String type,
+ final UiCallback<Message> callback) {
final String mimeType = MimeUtils.guessMimeTypeFromUriAndMime(this, uri, type);
final String compressPictures = getCompressPicturesPreference();
@@ -543,7 +609,10 @@ public class XmppConnectionService extends Service {
|| ("auto".equals(compressPictures) && getFileBackend().useImageAsIs(uri))
|| (mimeType != null && mimeType.endsWith("/gif"))
|| getFileBackend().unusualBounds(uri)) {
- Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": not compressing picture. sending as file");
+ Log.d(
+ Config.LOGTAG,
+ conversation.getAccount().getJid().asBareJid()
+ + ": not compressing picture. sending as file");
attachFileToConversation(conversation, uri, mimeType, callback);
return;
}
@@ -558,29 +627,33 @@ public class XmppConnectionService extends Service {
message.setType(Message.TYPE_IMAGE);
}
Log.d(Config.LOGTAG, "attachImage: type=" + message.getType());
- FILE_ATTACHMENT_EXECUTOR.execute(() -> {
- try {
- getFileBackend().copyImageToPrivateStorage(message, uri);
- } catch (FileBackend.ImageCompressionException e) {
- Log.d(Config.LOGTAG, "unable to compress image. fall back to file transfer", e);
- attachFileToConversation(conversation, uri, mimeType, callback);
- return;
- } catch (final FileBackend.FileCopyException e) {
- callback.error(e.getResId(), message);
- return;
- }
- if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
- final PgpEngine pgpEngine = getPgpEngine();
- if (pgpEngine != null) {
- pgpEngine.encrypt(message, callback);
- } else if (callback != null) {
- callback.error(R.string.unable_to_connect_to_keychain, null);
- }
- } else {
- sendMessage(message);
- callback.success(message);
- }
- });
+ FILE_ATTACHMENT_EXECUTOR.execute(
+ () -> {
+ try {
+ getFileBackend().copyImageToPrivateStorage(message, uri);
+ } catch (FileBackend.ImageCompressionException e) {
+ Log.d(
+ Config.LOGTAG,
+ "unable to compress image. fall back to file transfer",
+ e);
+ attachFileToConversation(conversation, uri, mimeType, callback);
+ return;
+ } catch (final FileBackend.FileCopyException e) {
+ callback.error(e.getResId(), message);
+ return;
+ }
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
+ final PgpEngine pgpEngine = getPgpEngine();
+ if (pgpEngine != null) {
+ pgpEngine.encrypt(message, callback);
+ } else if (callback != null) {
+ callback.error(R.string.unable_to_connect_to_keychain, null);
+ }
+ } else {
+ sendMessage(message);
+ callback.success(message);
+ }
+ });
}
public Conversation find(Bookmark bookmark) {
@@ -596,16 +669,26 @@ public class XmppConnectionService extends Service {
return c != null && c.getMode() == Conversational.MODE_MULTI;
}
- public void search(final List<String> term, final String uuid, final OnSearchResultsAvailable onSearchResultsAvailable) {
+ public void search(
+ final List<String> term,
+ final String uuid,
+ final OnSearchResultsAvailable onSearchResultsAvailable) {
MessageSearchTask.search(this, term, uuid, onSearchResultsAvailable);
}
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
final String action = Strings.nullToEmpty(intent == null ? null : intent.getAction());
- final boolean needsForegroundService = intent != null && intent.getBooleanExtra(SystemEventReceiver.EXTRA_NEEDS_FOREGROUND_SERVICE, false);
+ final boolean needsForegroundService =
+ intent != null
+ && intent.getBooleanExtra(
+ SystemEventReceiver.EXTRA_NEEDS_FOREGROUND_SERVICE, false);
if (needsForegroundService) {
- Log.d(Config.LOGTAG, "toggle forced foreground service after receiving event (action=" + action + ")");
+ Log.d(
+ Config.LOGTAG,
+ "toggle forced foreground service after receiving event (action="
+ + action
+ + ")");
toggleForegroundService(true);
}
final String uuid = intent == null ? null : intent.getStringExtra("uuid");
@@ -628,75 +711,93 @@ public class XmppConnectionService extends Service {
logoutAndSave(true);
return START_NOT_STICKY;
case ACTION_CLEAR_MESSAGE_NOTIFICATION:
- mNotificationExecutor.execute(() -> {
- try {
- final Conversation c = findConversationByUuid(uuid);
- if (c != null) {
- mNotificationService.clearMessages(c);
- } else {
- mNotificationService.clearMessages();
- }
- restoredFromDatabaseLatch.await();
+ mNotificationExecutor.execute(
+ () -> {
+ try {
+ final Conversation c = findConversationByUuid(uuid);
+ if (c != null) {
+ mNotificationService.clearMessages(c);
+ } else {
+ mNotificationService.clearMessages();
+ }
+ restoredFromDatabaseLatch.await();
- } catch (InterruptedException e) {
- Log.d(Config.LOGTAG, "unable to process clear message notification");
- }
- });
+ } catch (InterruptedException e) {
+ Log.d(
+ Config.LOGTAG,
+ "unable to process clear message notification");
+ }
+ });
break;
case ACTION_CLEAR_MISSED_CALL_NOTIFICATION:
- mNotificationExecutor.execute(() -> {
- try {
- final Conversation c = findConversationByUuid(uuid);
- if (c != null) {
- mNotificationService.clearMissedCalls(c);
- } else {
- mNotificationService.clearMissedCalls();
- }
- restoredFromDatabaseLatch.await();
+ mNotificationExecutor.execute(
+ () -> {
+ try {
+ final Conversation c = findConversationByUuid(uuid);
+ if (c != null) {
+ mNotificationService.clearMissedCalls(c);
+ } else {
+ mNotificationService.clearMissedCalls();
+ }
+ restoredFromDatabaseLatch.await();
- } catch (InterruptedException e) {
- Log.d(Config.LOGTAG, "unable to process clear missed call notification");
- }
- });
+ } catch (InterruptedException e) {
+ Log.d(
+ Config.LOGTAG,
+ "unable to process clear missed call notification");
+ }
+ });
break;
- case ACTION_DISMISS_CALL: {
- if (intent == null) {
+ case ACTION_DISMISS_CALL:
+ {
+ if (intent == null) {
+ break;
+ }
+ final String sessionId =
+ intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
+ Log.d(
+ Config.LOGTAG,
+ "received intent to dismiss call with session id " + sessionId);
+ mJingleConnectionManager.rejectRtpSession(sessionId);
break;
}
- final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
- Log.d(Config.LOGTAG, "received intent to dismiss call with session id " + sessionId);
- mJingleConnectionManager.rejectRtpSession(sessionId);
- break;
- }
case TorServiceUtils.ACTION_STATUS:
- final String status = intent == null ? null : intent.getStringExtra(TorServiceUtils.EXTRA_STATUS);
- //TODO port and host are in 'extras' - but this may not be a reliable source?
+ final String status =
+ intent == null ? null : intent.getStringExtra(TorServiceUtils.EXTRA_STATUS);
+ // TODO port and host are in 'extras' - but this may not be a reliable source?
if ("ON".equals(status)) {
handleOrbotStartedEvent();
return START_STICKY;
}
break;
- case ACTION_END_CALL: {
- if (intent == null) {
- break;
- }
- final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
- Log.d(Config.LOGTAG, "received intent to end call with session id " + sessionId);
- mJingleConnectionManager.endRtpSession(sessionId);
- }
- break;
- case ACTION_PROVISION_ACCOUNT: {
- if (intent == null) {
- break;
+ case ACTION_END_CALL:
+ {
+ if (intent == null) {
+ break;
+ }
+ final String sessionId =
+ intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
+ Log.d(
+ Config.LOGTAG,
+ "received intent to end call with session id " + sessionId);
+ mJingleConnectionManager.endRtpSession(sessionId);
}
- final String address = intent.getStringExtra("address");
- final String password = intent.getStringExtra("password");
- if (QuickConversationsService.isQuicksy() || Strings.isNullOrEmpty(address) || Strings.isNullOrEmpty(password)) {
+ break;
+ case ACTION_PROVISION_ACCOUNT:
+ {
+ if (intent == null) {
+ break;
+ }
+ final String address = intent.getStringExtra("address");
+ final String password = intent.getStringExtra("password");
+ if (QuickConversationsService.isQuicksy()
+ || Strings.isNullOrEmpty(address)
+ || Strings.isNullOrEmpty(password)) {
+ break;
+ }
+ provisionAccount(address, password);
break;
}
- provisionAccount(address, password);
- break;
- }
case ACTION_DISMISS_ERROR_NOTIFICATIONS:
dismissErrorNotifications();
break;
@@ -704,55 +805,75 @@ public class XmppConnectionService extends Service {
resetAllAttemptCounts(false, true);
break;
case ACTION_REPLY_TO_CONVERSATION:
- final Bundle remoteInput = intent == null ? null : RemoteInput.getResultsFromIntent(intent);
+ final Bundle remoteInput =
+ intent == null ? null : RemoteInput.getResultsFromIntent(intent);
if (remoteInput == null) {
break;
}
final CharSequence body = remoteInput.getCharSequence("text_reply");
- final boolean dismissNotification = intent.getBooleanExtra("dismiss_notification", false);
+ final boolean dismissNotification =
+ intent.getBooleanExtra("dismiss_notification", false);
final String lastMessageUuid = intent.getStringExtra("last_message_uuid");
if (body == null || body.length() <= 0) {
break;
}
- mNotificationExecutor.execute(() -> {
- try {
- restoredFromDatabaseLatch.await();
- final Conversation c = findConversationByUuid(uuid);
- if (c != null) {
- directReply(c, body.toString(), lastMessageUuid, dismissNotification);
- }
- } catch (InterruptedException e) {
- Log.d(Config.LOGTAG, "unable to process direct reply");
- }
- });
+ mNotificationExecutor.execute(
+ () -> {
+ try {
+ restoredFromDatabaseLatch.await();
+ final Conversation c = findConversationByUuid(uuid);
+ if (c != null) {
+ directReply(
+ c,
+ body.toString(),
+ lastMessageUuid,
+ dismissNotification);
+ }
+ } catch (InterruptedException e) {
+ Log.d(Config.LOGTAG, "unable to process direct reply");
+ }
+ });
break;
case ACTION_MARK_AS_READ:
- mNotificationExecutor.execute(() -> {
- final Conversation c = findConversationByUuid(uuid);
- if (c == null) {
- Log.d(Config.LOGTAG, "received mark read intent for unknown conversation (" + uuid + ")");
- return;
- }
- try {
- restoredFromDatabaseLatch.await();
- sendReadMarker(c, null);
- } catch (InterruptedException e) {
- Log.d(Config.LOGTAG, "unable to process notification read marker for conversation " + c.getName());
- }
-
- });
+ mNotificationExecutor.execute(
+ () -> {
+ final Conversation c = findConversationByUuid(uuid);
+ if (c == null) {
+ Log.d(
+ Config.LOGTAG,
+ "received mark read intent for unknown conversation ("
+ + uuid
+ + ")");
+ return;
+ }
+ try {
+ restoredFromDatabaseLatch.await();
+ sendReadMarker(c, null);
+ } catch (InterruptedException e) {
+ Log.d(
+ Config.LOGTAG,
+ "unable to process notification read marker for"
+ + " conversation "
+ + c.getName());
+ }
+ });
break;
case ACTION_SNOOZE:
- mNotificationExecutor.execute(() -> {
- final Conversation c = findConversationByUuid(uuid);
- if (c == null) {
- Log.d(Config.LOGTAG, "received snooze intent for unknown conversation (" + uuid + ")");
- return;
- }
- c.setMutedTill(System.currentTimeMillis() + 30 * 60 * 1000);
- mNotificationService.clearMessages(c);
- updateConversation(c);
- });
+ mNotificationExecutor.execute(
+ () -> {
+ final Conversation c = findConversationByUuid(uuid);
+ if (c == null) {
+ Log.d(
+ Config.LOGTAG,
+ "received snooze intent for unknown conversation ("
+ + uuid
+ + ")");
+ return;
+ }
+ c.setMutedTill(System.currentTimeMillis() + 30 * 60 * 1000);
+ mNotificationService.clearMessages(c);
+ updateConversation(c);
+ });
case AudioManager.RINGER_MODE_CHANGED_ACTION:
case NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED:
if (dndOnSilentMode()) {