Fix backup job background start restricitions with API31+.

This commit is contained in:
Cody Henthorne
2022-10-12 09:48:40 -04:00
committed by GitHub
parent e1c6dfb73b
commit a8e03e9bf2
18 changed files with 453 additions and 22 deletions

View File

@@ -1,10 +1,12 @@
package org.thoughtcrime.securesms.service;
import android.app.ForegroundServiceStartNotAllowedException;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import androidx.annotation.DrawableRes;
@@ -18,6 +20,7 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.MainActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.whispersystems.signalservice.api.util.Preconditions;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -126,20 +129,39 @@ public final class GenericForegroundService extends Service {
* Waits for {@param delayMillis} ms before starting the foreground task.
* <p>
* The delayed notification controller can also shown on demand and promoted to a regular notification controller to update the message etc.
*
* Do not call this method on API > 31
*/
public static DelayedNotificationController startForegroundTaskDelayed(@NonNull Context context, @NonNull String task, long delayMillis, @DrawableRes int iconRes) {
return DelayedNotificationController.create(delayMillis, () -> startForegroundTask(context, task, DEFAULTS.channelId, iconRes));
Preconditions.checkArgument(Build.VERSION.SDK_INT < 31);
return DelayedNotificationController.create(delayMillis, () -> {
try {
return startForegroundTask(context, task, DEFAULTS.channelId, iconRes);
} catch (UnableToStartException e) {
Log.w(TAG, "This should not happen on API < 31", e);
throw new AssertionError(e.getCause());
}
});
}
public static NotificationController startForegroundTask(@NonNull Context context, @NonNull String task) {
public static NotificationController startForegroundTask(@NonNull Context context, @NonNull String task) throws UnableToStartException {
return startForegroundTask(context, task, DEFAULTS.channelId);
}
public static NotificationController startForegroundTask(@NonNull Context context, @NonNull String task, @NonNull String channelId) {
public static NotificationController startForegroundTask(@NonNull Context context, @NonNull String task, @NonNull String channelId)
throws UnableToStartException
{
return startForegroundTask(context, task, channelId, DEFAULTS.iconRes);
}
public static NotificationController startForegroundTask(@NonNull Context context, @NonNull String task, @NonNull String channelId, @DrawableRes int iconRes) {
public static NotificationController startForegroundTask(
@NonNull Context context,
@NonNull String task,
@NonNull String channelId,
@DrawableRes int iconRes)
throws UnableToStartException
{
final int id = NEXT_ID.getAndIncrement();
Intent intent = new Intent(context, GenericForegroundService.class);
@@ -150,7 +172,17 @@ public final class GenericForegroundService extends Service {
intent.putExtra(EXTRA_ID, id);
Log.i(TAG, String.format(Locale.US, "Starting foreground service (%s) id=%d", task, id));
ContextCompat.startForegroundService(context, intent);
if (Build.VERSION.SDK_INT < 31) {
ContextCompat.startForegroundService(context, intent);
} else {
try {
ContextCompat.startForegroundService(context, intent);
} catch (ForegroundServiceStartNotAllowedException e) {
Log.e(TAG, "Unable to start foreground service", e);
throw new UnableToStartException(e);
}
}
return new NotificationController(context, id);
}
@@ -289,4 +321,10 @@ public final class GenericForegroundService extends Service {
return GenericForegroundService.this;
}
}
public static final class UnableToStartException extends Exception {
public UnableToStartException(Throwable cause) {
super(cause);
}
}
}

View File

@@ -3,19 +3,27 @@ package org.thoughtcrime.securesms.service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.jobs.LocalBackupJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.JavaTimeExtensionsKt;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
public class LocalBackupListener extends PersistentAlarmManagerListener {
private static final long INTERVAL = TimeUnit.DAYS.toMillis(1);
@Override
protected boolean scheduleExact() {
return Build.VERSION.SDK_INT >= 31;
}
@Override
protected long getNextScheduledExecutionTime(Context context) {
return TextSecurePreferences.getNextBackupTime(context);
@@ -24,7 +32,7 @@ public class LocalBackupListener extends PersistentAlarmManagerListener {
@Override
protected long onAlarm(Context context, long scheduledTime) {
if (SignalStore.settings().isBackupEnabled()) {
LocalBackupJob.enqueue(false);
LocalBackupJob.enqueue(scheduleExact());
}
return setNextBackupTimeToIntervalFromNow(context);
@@ -37,7 +45,20 @@ public class LocalBackupListener extends PersistentAlarmManagerListener {
}
public static long setNextBackupTimeToIntervalFromNow(@NonNull Context context) {
long nextTime = System.currentTimeMillis() + INTERVAL;
long nextTime;
if (Build.VERSION.SDK_INT < 31) {
nextTime = System.currentTimeMillis() + INTERVAL;
} else {
LocalDateTime now = LocalDateTime.now();
LocalDateTime next = now.withHour(2).withMinute(0).withSecond(0);
if (now.getHour() > 2) {
next = next.plusDays(1);
}
nextTime = JavaTimeExtensionsKt.toMillis(next);
}
TextSecurePreferences.setNextBackupTime(context, nextTime);
return nextTime;

View File

@@ -6,6 +6,7 @@ import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import org.signal.core.util.PendingIntentFlags;
import org.signal.core.util.logging.Log;
@@ -35,9 +36,21 @@ public abstract class PersistentAlarmManagerListener extends BroadcastReceiver {
if (pendingIntent != null) {
alarmManager.cancel(pendingIntent);
alarmManager.set(AlarmManager.RTC_WAKEUP, scheduledTime, pendingIntent);
if (scheduleExact() && Build.VERSION.SDK_INT >= 31) {
if (alarmManager.canScheduleExactAlarms()) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, scheduledTime, pendingIntent);
} else {
Log.w(TAG, "Unable to schedule exact alarm, permissionAllowed: " + alarmManager.canScheduleExactAlarms());
}
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, scheduledTime, pendingIntent);
}
} else {
Log.i(TAG, "PendingIntent somehow null, skipping");
}
}
protected boolean scheduleExact() {
return false;
}
}