Move all files to natural position.

This commit is contained in:
Alan Evans
2020-01-06 10:52:48 -05:00
parent 0df36047e7
commit 9ebe920195
3016 changed files with 6 additions and 36 deletions

View File

@@ -0,0 +1,71 @@
package org.thoughtcrime.securesms.gcm;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
/**
* Pulls down messages. Used when we fail to pull down messages in {@link FcmService}.
*/
@RequiresApi(26)
public class FcmJobService extends JobService {
private static final String TAG = FcmJobService.class.getSimpleName();
private static final int ID = 1337;
@RequiresApi(26)
public static void schedule(@NonNull Context context) {
JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(ID, new ComponentName(context, FcmJobService.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setBackoffCriteria(0, JobInfo.BACKOFF_POLICY_LINEAR)
.setPersisted(true);
ServiceUtil.getJobScheduler(context).schedule(jobInfoBuilder.build());
}
@Override
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "onStartJob()");
if (MessageRetriever.shouldIgnoreFetch(this)) {
Log.i(TAG, "App is foregrounded. No need to run.");
return false;
}
SignalExecutors.UNBOUNDED.execute(() -> {
Context context = getApplicationContext();
MessageRetriever retriever = ApplicationDependencies.getMessageRetriever();
boolean success = retriever.retrieveMessages(context, new RestStrategy(), new RestStrategy());
if (success) {
Log.i(TAG, "Successfully retrieved messages.");
jobFinished(params, false);
} else {
Log.w(TAG, "Failed to retrieve messages. Scheduling a retry.");
jobFinished(params, true);
}
});
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
Log.d(TAG, "onStopJob()");
return TextSecurePreferences.getNeedsMessagePull(getApplicationContext());
}
}

View File

@@ -0,0 +1,72 @@
package org.thoughtcrime.securesms.gcm;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.registration.PushChallengeRequest;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
public class FcmService extends FirebaseMessagingService {
private static final String TAG = FcmService.class.getSimpleName();
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.i(TAG, "FCM message... Delay: " + (System.currentTimeMillis() - remoteMessage.getSentTime()));
String challenge = remoteMessage.getData().get("challenge");
if (challenge != null) {
handlePushChallenge(challenge);
} else {
handleReceivedNotification(getApplicationContext());
}
}
@Override
public void onNewToken(String token) {
Log.i(TAG, "onNewToken()");
if (!TextSecurePreferences.isPushRegistered(getApplicationContext())) {
Log.i(TAG, "Got a new FCM token, but the user isn't registered.");
return;
}
ApplicationDependencies.getJobManager().add(new FcmRefreshJob());
}
private static void handleReceivedNotification(Context context) {
MessageRetriever retriever = ApplicationDependencies.getMessageRetriever();
boolean success = retriever.retrieveMessages(context, new RestStrategy(), new RestStrategy());
if (success) {
Log.i(TAG, "Successfully retrieved messages.");
} else {
if (Build.VERSION.SDK_INT >= 26) {
Log.w(TAG, "Failed to retrieve messages. Scheduling on the system JobScheduler (API " + Build.VERSION.SDK_INT + ").");
FcmJobService.schedule(context);
} else {
Log.w(TAG, "Failed to retrieve messages. Scheduling on JobManager (API " + Build.VERSION.SDK_INT + ").");
ApplicationDependencies.getJobManager().add(new PushNotificationReceiveJob(context));
}
}
Log.i(TAG, "Processing complete.");
}
private static void handlePushChallenge(@NonNull String challenge) {
Log.d(TAG, String.format("Got a push challenge \"%s\"", challenge));
PushChallengeRequest.postChallengeResponse(challenge);
}
}

View File

@@ -0,0 +1,44 @@
package org.thoughtcrime.securesms.gcm;
import androidx.annotation.WorkerThread;
import android.text.TextUtils;
import com.google.firebase.iid.FirebaseInstanceId;
import org.thoughtcrime.securesms.logging.Log;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
public final class FcmUtil {
private static final String TAG = FcmUtil.class.getSimpleName();
/**
* Retrieves the current FCM token. If one isn't available, it'll be generated.
*/
@WorkerThread
public static Optional<String> getToken() {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<String> token = new AtomicReference<>(null);
FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(task -> {
if (task.isSuccessful() && task.getResult() != null && !TextUtils.isEmpty(task.getResult().getToken())) {
token.set(task.getResult().getToken());
} else {
Log.w(TAG, "Failed to get the token.", task.getException());
}
latch.countDown();
});
try {
latch.await();
} catch (InterruptedException e) {
Log.w(TAG, "Was interrupted while waiting for the token.");
}
return Optional.fromNullable(token.get());
}
}

View File

@@ -0,0 +1,121 @@
package org.thoughtcrime.securesms.gcm;
import android.content.Context;
import android.os.PowerManager;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
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.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* Facilitates the retrieval of messages via provided {@link Strategy}'s.
*/
public class MessageRetriever {
private static final String TAG = Log.tag(MessageRetriever.class);
private static final String WAKE_LOCK_TAG = "MessageRetriever";
private static final Semaphore ACTIVE_LOCK = new Semaphore(2);
/**
* @return False if the retrieval failed and should be rescheduled, otherwise true.
*/
@WorkerThread
public boolean retrieveMessages(@NonNull Context context, Strategy... strategies) {
if (shouldIgnoreFetch(context)) {
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) {
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);
}
boolean success = false;
for (Strategy strategy : strategies) {
if (shouldIgnoreFetch(context)) {
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.run()) {
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;
} finally {
WakeLockUtil.release(wakeLock, WAKE_LOCK_TAG);
ACTIVE_LOCK.release();
}
}
}
/**
* @return True if there is no need to execute a message fetch, because the websocket will take
* care of it.
*/
public static boolean shouldIgnoreFetch(@NonNull Context context) {
return ApplicationContext.getInstance(context).isAppVisible() &&
!ApplicationDependencies.getSignalServiceNetworkAccess().isCensored(context);
}
private static String logSuffix(long startTime) {
return " (" + (System.currentTimeMillis() - startTime) + " ms elapsed)";
}
/**
* A method of retrieving messages.
*/
public interface Strategy {
/**
* @return False if the message retrieval failed and should be retried, otherwise true.
*/
@WorkerThread
boolean run();
}
}

View File

@@ -0,0 +1,110 @@
package org.thoughtcrime.securesms.gcm;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import org.thoughtcrime.securesms.IncomingMessageProcessor;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JobTracker;
import org.thoughtcrime.securesms.jobs.MarkerJob;
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
import org.thoughtcrime.securesms.jobs.PushProcessMessageJob;
import org.thoughtcrime.securesms.logging.Log;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Retrieves messages over the REST endpoint.
*/
public class RestStrategy implements MessageRetriever.Strategy {
private static final String TAG = Log.tag(RestStrategy.class);
private static final long SOCKET_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
@WorkerThread
@Override
public boolean run() {
long startTime = System.currentTimeMillis();
try (IncomingMessageProcessor.Processor processor = ApplicationDependencies.getIncomingMessageProcessor().acquire()) {
SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver();
AtomicInteger jobCount = new AtomicInteger(0);
receiver.setSoTimeoutMillis(SOCKET_TIMEOUT);
receiver.retrieveMessages(envelope -> {
Log.i(TAG, "Retrieved an envelope." + timeSuffix(startTime));
String jobId = processor.processEnvelope(envelope);
if (jobId != null) {
jobCount.incrementAndGet();
}
Log.i(TAG, "Successfully processed an envelope." + timeSuffix(startTime));
});
Log.d(TAG, jobCount.get() + " PushDecryptMessageJob(s) were enqueued.");
long timeRemainingMs = blockUntilQueueDrained(PushDecryptMessageJob.QUEUE, TimeUnit.SECONDS.toMillis(10));
if (timeRemainingMs > 0) {
blockUntilQueueDrained(PushProcessMessageJob.QUEUE, timeRemainingMs);
}
return true;
} catch (IOException e) {
Log.w(TAG, "Failed to retrieve messages. Resetting the SignalServiceMessageReceiver.", e);
ApplicationDependencies.resetSignalServiceMessageReceiver();
return false;
}
}
private static long blockUntilQueueDrained(@NonNull String queue, long timeoutMs) {
final JobManager jobManager = ApplicationDependencies.getJobManager();
final MarkerJob markerJob = new MarkerJob(queue);
jobManager.add(markerJob);
long startTime = System.currentTimeMillis();
CountDownLatch latch = new CountDownLatch(1);
jobManager.addListener(markerJob.getId(), new JobTracker.JobListener() {
@Override
public void onStateChanged(@NonNull JobTracker.JobState jobState) {
if (jobState.isComplete()) {
jobManager.removeListener(this);
latch.countDown();
}
}
});
try {
if (!latch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
Log.w(TAG, "Timed out waiting for " + queue + " job(s) to finish!");
return 0;
}
} catch (InterruptedException e) {
throw new AssertionError(e);
}
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
Log.d(TAG, "Waited " + duration + " ms for the " + queue + " job(s) to finish.");
return timeoutMs - duration;
}
private static String timeSuffix(long startTime) {
return " (" + (System.currentTimeMillis() - startTime) + " ms elapsed)";
}
@Override
public @NonNull String toString() {
return RestStrategy.class.getSimpleName();
}
}