Remove BackgroundMessageRetriever and clean up old code.

This commit is contained in:
Greyson Parrelli
2023-08-14 12:21:50 -04:00
committed by Cody Henthorne
parent 9f75c37331
commit b07d675bb4
14 changed files with 91 additions and 305 deletions

View File

@@ -1,144 +0,0 @@
package org.thoughtcrime.securesms.messages;
import android.content.Context;
import android.os.Build;
import android.os.PowerManager;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.service.DelayedNotificationController;
import org.thoughtcrime.securesms.service.GenericForegroundService;
import org.thoughtcrime.securesms.util.PowerManagerCompat;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.WakeLockUtil;
import java.io.Closeable;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* Retrieves messages while the app is in the background via provided {@link MessageRetrievalStrategy}'s.
*/
public class BackgroundMessageRetriever {
private static final String TAG = Log.tag(BackgroundMessageRetriever.class);
private static final String WAKE_LOCK_TAG = "MessageRetriever";
private static final Semaphore ACTIVE_LOCK = new Semaphore(2);
public static final long DO_NOT_SHOW_IN_FOREGROUND = DelayedNotificationController.DO_NOT_SHOW;
/**
* @return False if the retrieval failed and should be rescheduled, otherwise true.
*/
@WorkerThread
public boolean retrieveMessages(@NonNull Context context, MessageRetrievalStrategy... strategies) {
return retrieveMessages(context, DO_NOT_SHOW_IN_FOREGROUND, strategies);
}
/**
* @return False if the retrieval failed and should be rescheduled, otherwise true.
*/
@WorkerThread
public boolean retrieveMessages(@NonNull Context context, long showNotificationAfterMs, MessageRetrievalStrategy... strategies) {
if (shouldIgnoreFetch()) {
Log.i(TAG, "Skipping retrieval -- app is in the foreground.");
return true;
}
if (!ACTIVE_LOCK.tryAcquire()) {
Log.i(TAG, "Skipping retrieval -- there's already one enqueued.");
return true;
}
synchronized (this) {
try (NoExceptionCloseable unused = startDelayedForegroundServiceIfPossible(context, showNotificationAfterMs)) {
PowerManager.WakeLock wakeLock = null;
try {
wakeLock = WakeLockUtil.acquire(context, PowerManager.PARTIAL_WAKE_LOCK, TimeUnit.SECONDS.toMillis(60), WAKE_LOCK_TAG);
TextSecurePreferences.setNeedsMessagePull(context, true);
long startTime = System.currentTimeMillis();
PowerManager powerManager = ServiceUtil.getPowerManager(context);
boolean doze = PowerManagerCompat.isDeviceIdleMode(powerManager);
boolean network = new NetworkConstraint.Factory(ApplicationContext.getInstance(context)).create().isMet();
if (doze || !network) {
Log.w(TAG, "We may be operating in a constrained environment. Doze: " + doze + " Network: " + network);
}
Log.i(TAG, "Performing normal message fetch.");
return executeBackgroundRetrieval(context, startTime, strategies);
} finally {
WakeLockUtil.release(wakeLock, WAKE_LOCK_TAG);
ACTIVE_LOCK.release();
}
}
}
}
private NoExceptionCloseable startDelayedForegroundServiceIfPossible(@NonNull Context context, long showNotificationAfterMs) {
if (Build.VERSION.SDK_INT < 31) {
return GenericForegroundService.startForegroundTaskDelayed(context, context.getString(R.string.BackgroundMessageRetriever_checking_for_messages), showNotificationAfterMs, R.drawable.ic_signal_refresh)::close;
} else {
return () -> {};
}
}
private boolean executeBackgroundRetrieval(@NonNull Context context, long startTime, @NonNull MessageRetrievalStrategy[] strategies) {
boolean success = false;
for (MessageRetrievalStrategy strategy : strategies) {
if (shouldIgnoreFetch()) {
Log.i(TAG, "Stopping further strategy attempts -- app is in the foreground." + logSuffix(startTime));
success = true;
break;
}
Log.i(TAG, "Attempting strategy: " + strategy.toString() + logSuffix(startTime));
if (strategy.execute()) {
Log.i(TAG, "Strategy succeeded: " + strategy.toString() + logSuffix(startTime));
success = true;
break;
} else {
Log.w(TAG, "Strategy failed: " + strategy.toString() + logSuffix(startTime));
}
}
if (success) {
TextSecurePreferences.setNeedsMessagePull(context, false);
} else {
Log.w(TAG, "All strategies failed!" + logSuffix(startTime));
}
return success;
}
/**
* @return True if there is no need to execute a message fetch, because the websocket will take
* care of it.
*/
public static boolean shouldIgnoreFetch() {
return ApplicationDependencies.getAppForegroundObserver().isForegrounded();
}
private static String logSuffix(long startTime) {
return " (" + (System.currentTimeMillis() - startTime) + " ms elapsed)";
}
private interface NoExceptionCloseable extends AutoCloseable {
@Override
void close();
}
}

View File

@@ -192,7 +192,7 @@ class IncomingMessageObserver(private val context: Application) {
private fun isConnectionNecessary(): Boolean {
val timeIdle: Long
val keepAliveEntries: Set<Map.Entry<String, Long>>
val keepAliveEntries: Set<Pair<String, Long>>
val appVisibleSnapshot: Boolean
lock.withLock {
@@ -205,7 +205,7 @@ class IncomingMessageObserver(private val context: Application) {
Log.d(TAG, "Removed old keep web socket open requests.")
}
keepAliveEntries = keepAliveTokens.entries.toImmutableSet()
keepAliveEntries = keepAliveTokens.entries.map { it.key to it.value }.toImmutableSet()
}
val registered = SignalStore.account().isRegistered

View File

@@ -1,49 +0,0 @@
package org.thoughtcrime.securesms.messages;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JobTracker;
import org.thoughtcrime.securesms.jobs.MarkerJob;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
/**
* Implementations are responsible for fetching and processing a batch of messages.
*/
public abstract class MessageRetrievalStrategy {
/**
* Fetches and processes any pending messages. This method should block until the messages are
* actually stored and processed -- not just retrieved.
*
* @return True if everything was successful up until cancelation, false otherwise.
*/
@WorkerThread
abstract boolean execute();
protected static class QueueFindingJobListener implements JobTracker.JobListener {
private final Set<String> queues = new HashSet<>();
@Override
@AnyThread
public void onStateChanged(@NonNull Job job, @NonNull JobTracker.JobState jobState) {
synchronized (queues) {
queues.add(job.getParameters().getQueue());
}
}
@NonNull Set<String> getQueues() {
synchronized (queues) {
return new HashSet<>(queues);
}
}
}
}

View File

@@ -1,17 +1,32 @@
package org.thoughtcrime.securesms.messages;
import android.app.Application;
import android.content.Context;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import org.signal.core.util.Stopwatch;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JobTracker;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.jobs.MarkerJob;
import org.thoughtcrime.securesms.jobs.PushProcessMessageJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.NetworkUtil;
import org.thoughtcrime.securesms.util.PowerManagerCompat;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.WakeLockUtil;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -20,25 +35,46 @@ import java.util.concurrent.TimeUnit;
/**
* Retrieves messages over the websocket.
*/
public class WebSocketStrategy extends MessageRetrievalStrategy {
public class WebSocketStrategy {
private static final String TAG = Log.tag(WebSocketStrategy.class);
private static final String KEEP_ALIVE_TOKEN = "WebsocketStrategy";
private static final long QUEUE_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
private static final long QUEUE_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
private static final String WAKELOCK_PREFIX = "websocket-strategy-";
private final long websocketDrainTimeoutMs;
public WebSocketStrategy() {
this(TimeUnit.MINUTES.toMillis(1));
}
public WebSocketStrategy(long websocketDrainTimeoutMs) {
this.websocketDrainTimeoutMs = websocketDrainTimeoutMs;
@WorkerThread
public static boolean execute() {
return execute(TimeUnit.MINUTES.toMillis(1));
}
@WorkerThread
@Override
public boolean execute() {
public static boolean execute(long websocketDrainTimeoutMs) {
Application context = ApplicationDependencies.getApplication();
IncomingMessageObserver observer = ApplicationDependencies.getIncomingMessageObserver();
PowerManager powerManager = ServiceUtil.getPowerManager(context);
boolean doze = PowerManagerCompat.isDeviceIdleMode(powerManager);
boolean network = NetworkUtil.isConnected(context);
if (doze || !network) {
Log.w(TAG, "We may be operating in a constrained environment. Doze: " + doze + " Network: " + network);
}
observer.registerKeepAliveToken(KEEP_ALIVE_TOKEN);
String wakeLockTag = WAKELOCK_PREFIX + System.currentTimeMillis();
WakeLock wakeLock = WakeLockUtil.acquire(ApplicationDependencies.getApplication(), PowerManager.PARTIAL_WAKE_LOCK, websocketDrainTimeoutMs + QUEUE_TIMEOUT, wakeLockTag);
try {
return drainAndProcess(websocketDrainTimeoutMs);
} finally {
WakeLockUtil.release(wakeLock, wakeLockTag);
}
}
@WorkerThread
private static boolean drainAndProcess(long timeout) {
Stopwatch stopwatch = new Stopwatch("websocket-strategy");
IncomingMessageObserver observer = ApplicationDependencies.getIncomingMessageObserver();
@@ -49,7 +85,7 @@ public class WebSocketStrategy extends MessageRetrievalStrategy {
jobManager.addListener(job -> job.getParameters().getQueue() != null && job.getParameters().getQueue().startsWith(PushProcessMessageJob.QUEUE_PREFIX), queueListener);
if (!blockUntilWebsocketDrained(observer, websocketDrainTimeoutMs)) {
if (!blockUntilWebsocketDrained(observer, timeout)) {
return false;
}
@@ -115,8 +151,21 @@ public class WebSocketStrategy extends MessageRetrievalStrategy {
return true;
}
@Override
public @NonNull String toString() {
return Log.tag(WebSocketStrategy.class);
protected static class QueueFindingJobListener implements JobTracker.JobListener {
private final Set<String> queues = new HashSet<>();
@Override
@AnyThread
public void onStateChanged(@NonNull Job job, @NonNull JobTracker.JobState jobState) {
synchronized (queues) {
queues.add(job.getParameters().getQueue());
}
}
@NonNull Set<String> getQueues() {
synchronized (queues) {
return new HashSet<>(queues);
}
}
}
}