Fix issue with 500+ scheduled alarms
Currently there's an exception when more than 500 alarms are scheduled.
java.lang.IllegalStateException: Maximum limit of concurrent alarms 500 reached for uid: u0a153, callingPackage: at.techbee.jtx at android.os.Parcel.createExceptionOrNull(Parcel.java:2435) at android.os.Parcel.createException(Parcel.java:2411) at android.os.Parcel.readException(Parcel.java:2394) at android.os.Parcel.readException(Parcel.java:2336) at android.app.IAlarmManager$Stub$Proxy.set(IAlarmManager.java:359) at android.app.AlarmManager.setImpl(AlarmManager.java:956) at android.app.AlarmManager.setImpl(AlarmManager.java:908) at android.app.AlarmManager.set(AlarmManager.java:434) at at.techbee.jtx.database.properties.Alarm.scheduleNotification(Alarm.kt:444) at at.techbee.jtx.database.ICalObject.recreateRecurring(ICalObject.kt:1104) at at.techbee.jtx.ui.IcalEditViewModel$update$1.invokeSuspend(IcalEditViewModel.kt:382) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42) at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664) Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@d57caa, Dispatchers.IO] Caused by: android.os.RemoteException: Remote stack trace: at com.android.server.alarm.AlarmManagerService.setImpl(AlarmManagerService.java:2034) at com.android.server.alarm.AlarmManagerService$5.set(AlarmManagerService.java:2575) at android.app.IAlarmManager$Stub.onTransact(IAlarmManager.java:194) at android.os.Binder.execTransactInternal(Binder.java:1201) at android.os.Binder.execTransact(Binder.java:1164)
@ArnyminerZ may I ask you for your opinion on this? The problem is obviously, that you cannot schedule more than 500 alarms.
This can happen when you have recurring tasks with an alarm. But also when you have a lot of changing tasks with an alarm. When a recurring task is changed, all instances get recreated (ie. the existing alarms get deleted and new ones with a new id get generated). The same for the sync, when an entry was changed on the server, the main entry is updated, but all attributes that can be a list (like alarms) are deleted and inserted again. However, this seemed to be the more efficient way than coming up with a delta logic checking all list attributes one by one if they exist either locally or on the server and update them if necessary.
So alarms are always scheduled, but when Alarms are deleted, they are deleted through their foreign key and not one by one. The scheduled alarm stays in the (Android) system. When it triggers, the logic just checks if it is still in the database and only shows the notification, if it's still there. Otherwise it's just ignored.
I think the logic is basically still fine. But the solution for the 500 scheduled alarms error is a bit tricky.
So here's what I've been thinking of:
- Option 1: Run a daily job, delete all scheduled alarms and set alarms again. Catch the 500 alarms error and ignore it (as nobody hopefully would have 500 alarms in one day). The process of determining alarms might cause some effort in the background, but only once per day.
- Option 2: Every time an alarm is entered, deleted or updated, all alarms are cancelled and the next alarm from now is determined. Determining the next alarm could be laborous: You have to go through all alarms in the Alarms table, additionally you have to fetch the according icalobject (if the alarm is relative to the start or end date) and calculate the absolute date-time. Then determine the closest one in the future. It seems like a lot of effort after every change.
@ArnyminerZ do you maybe have another approach that I'm not thinking of? Or would you also agree with Option 1?