Use foreground services to process notification when appropriate.

Right now, the only condition is once every 3 minutes on Android 12.

This is ok because Android 12 will allow us (once every 2 minutes or
so) to start a foreground service, and it won't show it for the first 10
seconds. So we can kind of do it without any visual penalty.
This commit is contained in:
Greyson Parrelli 2022-04-18 11:27:32 -04:00 committed by GitHub
parent 8cfc013960
commit e09ce4c820
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 6 deletions

View file

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.gcm; package org.thoughtcrime.securesms.gcm;
import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -8,15 +9,21 @@ import android.os.IBinder;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.google.firebase.messaging.RemoteMessage; import com.google.firebase.messaging.RemoteMessage;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.MainActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob; import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.messages.BackgroundMessageRetriever; import org.thoughtcrime.securesms.messages.BackgroundMessageRetriever;
import org.thoughtcrime.securesms.messages.RestStrategy; import org.thoughtcrime.securesms.messages.RestStrategy;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.notifications.NotificationIds;
import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor; import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -43,10 +50,18 @@ public class FcmFetchService extends Service {
private static final String TAG = Log.tag(FcmFetchService.class); private static final String TAG = Log.tag(FcmFetchService.class);
static final String KEY_FOREGROUND = "is_foreground";
private static final SerialMonoLifoExecutor EXECUTOR = new SerialMonoLifoExecutor(SignalExecutors.UNBOUNDED); private static final SerialMonoLifoExecutor EXECUTOR = new SerialMonoLifoExecutor(SignalExecutors.UNBOUNDED);
private final AtomicInteger activeCount = new AtomicInteger(0); private final AtomicInteger activeCount = new AtomicInteger(0);
public static @NonNull Intent buildIntent(@NonNull Context context, boolean foreground) {
Intent intent = new Intent(context, FcmFetchService.class);
intent.putExtra(KEY_FOREGROUND, foreground);
return intent;
}
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
boolean performedReplace = EXECUTOR.enqueue(this::fetch); boolean performedReplace = EXECUTOR.enqueue(this::fetch);
@ -58,6 +73,18 @@ public class FcmFetchService extends Service {
Log.i(TAG, "Incrementing active count to " + count); Log.i(TAG, "Incrementing active count to " + count);
} }
if (intent.getBooleanExtra(KEY_FOREGROUND, false)) {
Log.i(TAG, "Launching in the foreground.");
startForeground(NotificationIds.FCM_FETCH, new NotificationCompat.Builder(this, NotificationChannels.OTHER)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(getString(R.string.BackgroundMessageRetriever_checking_for_messages))
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setProgress(0, 0, true)
.setContentIntent(PendingIntent.getActivity(this, 0, MainActivity.clearTop(this), 0))
.setVibrate(new long[] { 0 })
.build());
}
return START_NOT_STICKY; return START_NOT_STICKY;
} }

View file

@ -2,8 +2,11 @@ package org.thoughtcrime.securesms.gcm;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage; import com.google.firebase.messaging.RemoteMessage;
@ -16,11 +19,13 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.registration.PushChallengeRequest; import org.thoughtcrime.securesms.registration.PushChallengeRequest;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.TimeUnit;
public class FcmReceiveService extends FirebaseMessagingService { public class FcmReceiveService extends FirebaseMessagingService {
private static final String TAG = Log.tag(FcmReceiveService.class); private static final String TAG = Log.tag(FcmReceiveService.class);
private static final long FCM_FOREGROUND_INTERVAL = TimeUnit.MINUTES.toMillis(3);
@Override @Override
public void onMessageReceived(RemoteMessage remoteMessage) { public void onMessageReceived(RemoteMessage remoteMessage) {
@ -38,15 +43,15 @@ public class FcmReceiveService extends FirebaseMessagingService {
handleRegistrationPushChallenge(registrationChallenge); handleRegistrationPushChallenge(registrationChallenge);
} else if (rateLimitChallenge != null) { } else if (rateLimitChallenge != null) {
handleRateLimitPushChallenge(rateLimitChallenge); handleRateLimitPushChallenge(rateLimitChallenge);
}else { } else {
handleReceivedNotification(ApplicationDependencies.getApplication()); handleReceivedNotification(ApplicationDependencies.getApplication(), remoteMessage);
} }
} }
@Override @Override
public void onDeletedMessages() { public void onDeletedMessages() {
Log.w(TAG, "onDeleteMessages() -- Messages may have been dropped. Doing a normal message fetch."); Log.w(TAG, "onDeleteMessages() -- Messages may have been dropped. Doing a normal message fetch.");
handleReceivedNotification(ApplicationDependencies.getApplication()); handleReceivedNotification(ApplicationDependencies.getApplication(), null);
} }
@Override @Override
@ -71,11 +76,17 @@ public class FcmReceiveService extends FirebaseMessagingService {
Log.w(TAG, "onSendError()", e); Log.w(TAG, "onSendError()", e);
} }
private static void handleReceivedNotification(Context context) { private static void handleReceivedNotification(Context context, @Nullable RemoteMessage remoteMessage) {
try { try {
context.startService(new Intent(context, FcmFetchService.class)); long timeSinceLastRefresh = System.currentTimeMillis() - SignalStore.misc().getLastFcmForegroundServiceTime();
if (Build.VERSION.SDK_INT >= 31 && remoteMessage != null && remoteMessage.getPriority() == RemoteMessage.PRIORITY_HIGH && timeSinceLastRefresh > FCM_FOREGROUND_INTERVAL) {
ContextCompat.startForegroundService(context, FcmFetchService.buildIntent(context, true));
SignalStore.misc().setLastFcmForegroundServiceTime(System.currentTimeMillis());
} else {
context.startService(FcmFetchService.buildIntent(context, false));
}
} catch (Exception e) { } catch (Exception e) {
Log.w(TAG, "Failed to start service. Falling back to legacy approach."); Log.w(TAG, "Failed to start service. Falling back to legacy approach.", e);
FcmFetchService.retrieveMessages(context); FcmFetchService.retrieveMessages(context);
} }
} }

View file

@ -21,6 +21,7 @@ public final class MiscellaneousValues extends SignalStoreValues {
private static final String CENSORSHIP_SERVICE_REACHABLE = "misc.censorship.service_reachable"; private static final String CENSORSHIP_SERVICE_REACHABLE = "misc.censorship.service_reachable";
private static final String LAST_GV2_PROFILE_CHECK_TIME = "misc.last_gv2_profile_check_time"; private static final String LAST_GV2_PROFILE_CHECK_TIME = "misc.last_gv2_profile_check_time";
private static final String CDS_TOKEN = "misc.cds_token"; private static final String CDS_TOKEN = "misc.cds_token";
private static final String LAST_FCM_FOREGROUND_TIME = "misc.last_fcm_foreground_time";
MiscellaneousValues(@NonNull KeyValueStore store) { MiscellaneousValues(@NonNull KeyValueStore store) {
super(store); super(store);
@ -149,4 +150,12 @@ public final class MiscellaneousValues extends SignalStoreValues {
.putBlob(CDS_TOKEN, token) .putBlob(CDS_TOKEN, token)
.commit(); .commit();
} }
public long getLastFcmForegroundServiceTime() {
return getLong(LAST_FCM_FOREGROUND_TIME, 0);
}
public void setLastFcmForegroundServiceTime(long time) {
putLong(LAST_FCM_FOREGROUND_TIME, time);
}
} }

View file

@ -14,6 +14,7 @@ public final class NotificationIds {
public static final int USER_NOTIFICATION_MIGRATION = 525600; public static final int USER_NOTIFICATION_MIGRATION = 525600;
public static final int DEVICE_TRANSFER = 625420; public static final int DEVICE_TRANSFER = 625420;
public static final int DONOR_BADGE_FAILURE = 630001; public static final int DONOR_BADGE_FAILURE = 630001;
public static final int FCM_FETCH = 630002;
private NotificationIds() { } private NotificationIds() { }