diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java index 0988a23c61..fdaed3aa1f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java @@ -5,6 +5,7 @@ import android.annotation.SuppressLint; import android.net.Uri; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.documentfile.provider.DocumentFile; import androidx.documentfile.provider.DocumentFileHelper; @@ -93,14 +94,23 @@ public final class LocalBackupJobApi29 extends BaseJob { } ProgressUpdater updater = new ProgressUpdater(context.getString(R.string.LocalBackupJob_verifying_signal_backup)); - try (NotificationController notification = GenericForegroundService.startForegroundTask(context, - context.getString(R.string.LocalBackupJob_creating_signal_backup), - NotificationChannels.BACKUPS, - R.drawable.ic_signal_backup)) - { + + NotificationController notification = null; + try { + notification = GenericForegroundService.startForegroundTask(context, + context.getString(R.string.LocalBackupJob_creating_signal_backup), + NotificationChannels.BACKUPS, + R.drawable.ic_signal_backup); + } catch (GenericForegroundService.UnableToStartException e) { + Log.w(TAG, "Unable to start foreground backup service, continuing without service"); + } + + try { updater.setNotification(notification); EventBus.getDefault().register(updater); - notification.setIndeterminateProgress(); + if (notification != null) { + notification.setIndeterminateProgress(); + } String backupPassword = BackupPassphrase.get(context); DocumentFile backupDirectory = DocumentFile.fromTreeUri(context, backupDirectoryUri); @@ -171,10 +181,10 @@ public final class LocalBackupJobApi29 extends BaseJob { } BackupUtil.deleteOldBackups(); - } catch (GenericForegroundService.UnableToStartException e) { - Log.w(TAG, "Unable to start foreground backup service", e); - BackupFileIOError.UNKNOWN.postNotification(context); } finally { + if (notification != null) { + notification.close(); + } EventBus.getDefault().unregister(updater); updater.setNotification(null); } @@ -266,7 +276,7 @@ public final class LocalBackupJobApi29 extends BaseJob { } } - public void setNotification(NotificationController notification) { + public void setNotification(@Nullable NotificationController notification) { this.notification = notification; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/DirectoryRefreshListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/DirectoryRefreshListener.java index c291a1cf82..46c9224be8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/DirectoryRefreshListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/DirectoryRefreshListener.java @@ -34,6 +34,6 @@ public class DirectoryRefreshListener extends PersistentAlarmManagerListener { } public static void schedule(Context context) { - new DirectoryRefreshListener().onReceive(context, new Intent()); + new DirectoryRefreshListener().onReceive(context, getScheduleIntent()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java index a11371c370..a8d68c2541 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.service; import android.content.Context; -import android.content.Intent; import android.os.Build; import androidx.annotation.NonNull; @@ -20,7 +19,7 @@ public class LocalBackupListener extends PersistentAlarmManagerListener { private static final long INTERVAL = TimeUnit.DAYS.toMillis(1); @Override - protected boolean scheduleExact() { + protected boolean shouldScheduleExact() { return Build.VERSION.SDK_INT >= 31; } @@ -32,7 +31,7 @@ public class LocalBackupListener extends PersistentAlarmManagerListener { @Override protected long onAlarm(Context context, long scheduledTime) { if (SignalStore.settings().isBackupEnabled()) { - LocalBackupJob.enqueue(scheduleExact()); + LocalBackupJob.enqueue(shouldScheduleExact()); } return setNextBackupTimeToIntervalFromNow(context); @@ -40,7 +39,7 @@ public class LocalBackupListener extends PersistentAlarmManagerListener { public static void schedule(Context context) { if (SignalStore.settings().isBackupEnabled()) { - new LocalBackupListener().onReceive(context, new Intent()); + new LocalBackupListener().onReceive(context, getScheduleIntent()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/PersistentAlarmManagerListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/PersistentAlarmManagerListener.java index 36f1586671..c430089a38 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/PersistentAlarmManagerListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/PersistentAlarmManagerListener.java @@ -8,49 +8,89 @@ import android.content.Context; import android.content.Intent; import android.os.Build; +import androidx.annotation.NonNull; + import org.signal.core.util.PendingIntentFlags; import org.signal.core.util.logging.Log; public abstract class PersistentAlarmManagerListener extends BroadcastReceiver { - private static final String TAG = Log.tag(PersistentAlarmManagerListener.class); + private static final String TAG = Log.tag(PersistentAlarmManagerListener.class); + private static final String ACTION_SCHEDULE = "signal.ACTION_SCHEDULE"; + + protected static @NonNull Intent getScheduleIntent() { + Intent scheduleIntent = new Intent(); + scheduleIntent.setAction(ACTION_SCHEDULE); + return scheduleIntent; + } protected abstract long getNextScheduledExecutionTime(Context context); + protected abstract long onAlarm(Context context, long scheduledTime); @Override public void onReceive(Context context, Intent intent) { - Log.i(TAG, String.format("%s#onReceive(%s)", getClass().getSimpleName(), intent.getAction())); + info(String.format("onReceive(%s)", intent.getAction())); long scheduledTime = getNextScheduledExecutionTime(context); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent alarmIntent = new Intent(context, getClass()); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntentFlags.immutable()); - - if (System.currentTimeMillis() >= scheduledTime) { + if (System.currentTimeMillis() >= scheduledTime && canRunDuringSchedule(intent.getAction())) { + info("onAlarm(): scheduled: " + scheduledTime + " actual: " + System.currentTimeMillis()); scheduledTime = onAlarm(context, scheduledTime); } - Log.i(TAG, getClass() + " scheduling for: " + scheduledTime + " action: " + intent.getAction()); + if (pendingIntent == null) { + info("PendingIntent somehow null, skipping"); + return; + } - if (pendingIntent != null) { - alarmManager.cancel(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); - } + alarmManager.cancel(pendingIntent); + + if (shouldScheduleExact()) { + scheduleExact(alarmManager, scheduledTime, pendingIntent); } else { - Log.i(TAG, "PendingIntent somehow null, skipping"); + info("scheduling alarm for: " + scheduledTime); + alarmManager.set(AlarmManager.RTC_WAKEUP, scheduledTime, pendingIntent); } } - protected boolean scheduleExact() { + private boolean canRunDuringSchedule(@NonNull String action) { + return !shouldScheduleExact() || !ACTION_SCHEDULE.equals(action); + } + + private void scheduleExact(AlarmManager alarmManager, long scheduledTime, PendingIntent pendingIntent) { + boolean hasManagerPermission = Build.VERSION.SDK_INT < 31 || alarmManager.canScheduleExactAlarms(); + + info("scheduling exact alarm for: " + scheduledTime + " hasManagerPermission: " + hasManagerPermission); + if (hasManagerPermission) { + try { + alarmManager.setExact(AlarmManager.RTC_WAKEUP, scheduledTime, pendingIntent); + return; + } catch (Exception e) { + warn(e); + } + } + + warn("Unable to schedule exact alarm, falling back to inexact alarm, scheduling alarm for: " + scheduledTime); + alarmManager.set(AlarmManager.RTC_WAKEUP, scheduledTime, pendingIntent); + } + + protected boolean shouldScheduleExact() { return false; } + + private void info(String message) { + Log.i(TAG, "[" + getClass().getSimpleName() + "] " + message); + } + + private void warn(String message) { + Log.w(TAG, "[" + getClass().getSimpleName() + "] " + message); + } + + private void warn(Throwable t) { + Log.w(TAG, "[" + getClass().getSimpleName() + "]", t); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/RotateSenderCertificateListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/RotateSenderCertificateListener.java index 9f8c0805d3..9e2fc54935 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/RotateSenderCertificateListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/RotateSenderCertificateListener.java @@ -30,7 +30,7 @@ public class RotateSenderCertificateListener extends PersistentAlarmManagerListe } public static void schedule(Context context) { - new RotateSenderCertificateListener().onReceive(context, new Intent()); + new RotateSenderCertificateListener().onReceive(context, getScheduleIntent()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java index 7c8d524611..ec6e394ab6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java @@ -33,6 +33,6 @@ public class RotateSignedPreKeyListener extends PersistentAlarmManagerListener { } public static void schedule(Context context) { - new RotateSignedPreKeyListener().onReceive(context, new Intent()); + new RotateSignedPreKeyListener().onReceive(context, getScheduleIntent()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/UpdateApkRefreshListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/UpdateApkRefreshListener.java index 9a580f88b3..c456de19a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/UpdateApkRefreshListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/UpdateApkRefreshListener.java @@ -39,7 +39,7 @@ public class UpdateApkRefreshListener extends PersistentAlarmManagerListener { } public static void schedule(Context context) { - new UpdateApkRefreshListener().onReceive(context, new Intent()); + new UpdateApkRefreshListener().onReceive(context, getScheduleIntent()); } }