
Android — Scheduling Alarms with Precise Delivery Time using AlarmManager
I have spent a good amount of time trying to find a way to schedule notifications with precise delivery time in one of the projects I have been working on. It wasn’t an easy task due to the variety of classes, methods you can use and also the difference in behaviors they have, which can be tricky, especially after Google decided to make alarms delivery inexact since API 19, along with Idle/App Standby mode restrictions implemented in API 23, in order to improve battery life management.
AlarmManager
Alarm Manager is a class that enables your application to be run somewhen in the future. The Alarm Manager Service maintains alarms even while the device is asleep, but once the device is turned off or rebooted, they are cleared (although you can change this action).
Because of its purpose of offering a service that allows tasks to be triggered by your app even when the app is not running, Alarm Manager does require a lot of interaction with the Android OS underlying architecture and restrictions, and this is what makes its usage difficult some times, especially when we are working with activities that should be run with delivery time precision.
Flags
Here lies the crux of the matter. Flags play an essential role in alarm’s workflow by communicating to the OS whether the alarm can be rearranged, wake up the device and so on. Here are some important ones to know:
FLAG_ALLOW_WHILE_IDLE — States that the alarm would like to execute even when the device is in idle mode. This alarm will be allowed to be executed but won’t take the device out of idle mode. Also, the OS may impose restrictions regarding the frequency that apps can schedule alarms with this flag, which can result in delays from 1 to 9 minutes for the alarm to trigger.
FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED — Works just like FLAG_ALLOW_WHILE_IDLE but with no frequency restrictions. This flag is only available for system alarms.
FLAG_STANDALONE — Flags for stand-alone alarms, which means that the alarm should not be batched with other alarms.
FLAG_WAKE_FROM_IDLE — States that the alarm would like to wake the device even in idle mode.
FLAG_IDLE_UNTIL — This flag states that the alarm would like the device to get out of idle mode at a certain time. The alarm manager is allowed to move this alarm to match WAKE_FROM_IDLE the first alarms.
Alarm Types
ELAPSED_REALTIME — Bases the trigger time of the alarm on the elapsedRealTime() method, which returns the number of milliseconds since boot, including the time that the device spent sleeping.
ELAPSED_REALTIME_WAKEUP — Works just like ELAPSED_REALTIME but wakes up the device when it runs.
RTC — Bases the trigger time of the alarm on the system’s currentTimeInMillis (UTC).
RTC_WAKEUP — Same as RTC but wakes up the device when it's asleep.
Doze mode
Doze mode has been introduced back in Android 6.0 (API 23) as a new feature for power-saving purposes. It prevents apps from using the CPU and network, and it’s activated when the device is left unplugged and with the screen turned off for a long period. Once the device is in Doze mode, it periodically opens a short “maintenance window”, which allows applications to complete tasks — it’s within these windows that normal alarms, syncs, jobs, etc. are allowed to be run. It’s important to note that maintenance windows are rescheduled less and less often, each time a new maintenance window is called, the next one will take longer to be invoked.
Doze mode imposes many restrictions, here we will pay attention to the restrictions that affect alarms, they are (from Android’s documentation):
- Standard AlarmManager alarms (including setExact() and setWindow()) are deferred to the next maintenance window.
- If you need to set alarms that fire while in Doze, use setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
- Alarms set with setAlarmClock() continue to fire normally — the system exits Doze shortly before those alarms fire.
What’s up for grabs in terms of precision
AlarmManager does offer you various options to schedule an alarm, each of them with different behaviors and specificities, they do that by combining different Flags with Alarm Types, as we described briefly in this article. Below you will find how some of Alarm Manager methods for scheduling an alarm work in terms of triggering time precision — I have ignored methods that don’t allow the clock to work on idle mode, as it implies imprecision.
setAndAllowWhileIdle
Exact: No
Android ensures that the alarm won’t be delivered before the specified trigger time. It also guarantees that the alarm will be triggered somewhen in the future, but not necessarily in the specific trigger time.
setExactAndAllowWhileIdle
Exact: Yes, but not really
setExactAndAllowWhileIdle is misleading: According to Android’s documentation, this method should set an alarm “to be delivered precisely at the stated time”, which is later described as “as nearly as possible to the requested trigger time” — so it’s not as exact as its name indicates. Some other details that make this method not exactly exact are:
- It’s restricted: Doze/Standby documentation states that alarms of this type can not be fired by the same app more than once per 9 minutes.
- It may not be ordered: The OS may reschedule this kind of alarm to be triggered out of order from other alarms, even from the same app.
- It allows flexibility: Android considers that by using this method, you opted for letting the OS be more flexible when scheduling the alarm than regular alarms. Also, the documentation states that “When the device is idle it may take even more liberties with scheduling to optimize for battery life.”
There are many reports from the developers’ community about this method not triggering the alarm at the exact stated time, I have experienced delays from 9 minutes to the alarm not being triggered at all with this method.
setAlarmClock
Exact: Yes
This has been the most accurate method I found. setAlarmClock is an interface that works mostly like setExactAndAllowWhileIdle, the main difference in its behavior is that it requests an AlarmClockInfo, indicating that the alarm has to be accurate and is permitted to wake up the system by setting two important flags: FLAG_WAKE_FROM_IDLE and FLAG_STANDALONE. The first lets the alarm wake up the device from idle mode and most importantly, the second flag keeps the OS from batching the alarm.
It’s important to note that this method sacrifices battery savings on devices, some developers even reported that this method prevents devices from entering idle mode when the alarm’s trigger time is close to the current time.
Decision flowchart
As pointed by Pablo Guardiola on his blog post “Da Real Fragmentation — Alarms”, fragmentation is a real problem that elevates the complexity of decision-making when it comes to scheduling alarms with Alarm Manager — it feels essentially like a Hobson’s choice in many situations. Pablo also presents a very useful flowchart for assisting developers to make their decision when using the Alarm Manager. I took the liberty of adapting his flowchart, but now including a few decisions and adding setAlarmClock as an option.

What about you? Have you faced this same issue with Alarm Manager already? What did you do to overcome it?
References:
