53

AlarmManager on API19 has the method setExact() to set an exact alarm.

Exact means --> If I set an alarm to 2:01 pm it will be triggered at 2:01 pm

On API 23 - Marhsmwallow (6.0) there is a new method setExactAndAllowWhileIdle(), but as of the reference it is not EXACT because it will trigger only every minute and in low power idle mode only every 15 minutes.

Exact != every 15 minutes :-)

So how can I achieve an exact alarm with AlarmManager in 6.0?

If a user adds a reminder or a calendar appointment and wants to be informed 10 minutes before the event it should show the alarm EXACT 10 minutes before the event. With setExactAndAllowWhileIdle() this seems is not possible.

Reference Link: http://developer.android.com/reference/android/app/AlarmManager.html#setExactAndAllowWhileIdle(int, long, android.app.PendingIntent)

chrisonline
  • 6,949
  • 11
  • 42
  • 62
  • 11
    I tested the calendar event scenario with Google Calendar. I'm on a Nexus 5 with the final OTA M release. At 10:00, I scheduled two events, one at 11:15 and the other at 11:20 (both to notify me at the start of the event). Then I left the device alone on the table. The first notification arrived at 11:15 exactly (this is documented, the first alarm is allowed to get through exactly), but I didn't touch the device and then the second one didn't arrive until 11:30. So, it looks like while in doze mode we depend on the user to manually wake the device up if we want events to show up on time. – jmart Oct 15 '15 at 14:46
  • 16
    Wow, Google even crippled their own app. Well done. – sec_aw Oct 15 '15 at 23:22
  • Can anybody please conclude all the facts about this issue? – Mehul Joisar Oct 27 '15 at 06:04
  • @MehulJoisar The 15 minute limitation is real and is here to stay (you can see the details on this talk: https://www.youtube.com/watch?v=Rwshwq_vI1s&index=23&list=PLWz5rJ2EKKc_HyE1QX9heAgTPdAMqc50z). So I'd say the best answer is the one provided by CommonsWare. – jmart Oct 27 '15 at 10:26
  • 3
    @jmart: OK so every "Reminder" app out there can't remind a user within 15 minutes more than once!! Sorry, battery life is good, but such an bad idea. All "reminder" apps are now the bad player. If a user wants to be reminded at 2 p.m. and at 2:05 p.m. it is not possible anymore!! Great user experience!! Google why did you do this? – chrisonline Oct 27 '15 at 10:36
  • 1
    @chrisonline I absolutely agree. The idea of penalizing all apps because some of them were misbehaving is not right. At least they should've given the user the chance to decide. But in the end if they want it this way, so be it. I'm sure they'll be the firsts to receive bad feedback (Google Calendar is the most installed calendar). On our side, as developers, our task will be to educate our users and let them know why this happens. As a user, this makes me lose faith on Android devices, because I want my device to always be on time no matter what. I can see Google backpedaling in the long run. – jmart Oct 27 '15 at 11:29
  • 1
    Yes, even many more use-cases are broken, those are not 'edge-cases' as stated in the documentation. Did you come across this thread? https://code.google.com/p/android-developer-preview/issues/detail?id=2225 It started well before the final release. So I wouldn't count on Google reversing things in the future. There are also other issues with Doze, that are not documented at all, e.g. hardware features like GPS not being accessible as shown in my test logs here http://stackoverflow.com/a/32521940/4301846 In my view the current implementation of Doze is a failed concept for many reasons. – sec_aw Oct 28 '15 at 11:27
  • 2
    Tested setExactAndAllowWhileIdle - on Galaxy S8 with Android 8.0.0. A new alarm was created using setExactAndAllowWhileIdle each time the alarm went off. Phone left untouched and allowed to go into doze. After firing on schedule 3 times, the alarm stopped firing altogether until I manually woke the phone up. This method is altogether unreliable for exact alarms. – user1608385 Apr 16 '18 at 15:52

4 Answers4

26

So how can I achieve an exact alarm with AlarmManager in 6.0?

You are welcome to try setAlarmClock(), as AFAIK it is unaffected by Doze mode. Otherwise, AlarmManager is not a viable option for you. Even having your app on the battery optimization whitelist will not help, as AlarmManager behavior does not change based on the whitelist.

You are welcome to use GCM, as a high-priority message should give you an opportunity to alert the user. This, of course, requires network connectivity.

The only offline solution that I am aware of — and that I am presently testing — is to have the user add your app to the battery optimization whitelist, then use a foreground service (to try to keep your process around), a ScheduledExecutorService (for the timing), and a partial WakeLock (to keep the CPU on). This will be fairly devastating to the user's battery.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 13
    Oh Google this is a bad move. Creating a new method "setExactAndAllowWhileIdle()", but it is not exact.. Sorry seems a little bit strange to me. And all calendar apps out there have to use "setAlarmClock()" for displaying a normal notification about an upcoming appointment?!? And the reference has as an example the calendar appoinment at the setExactAndAllowWhileIdle() mehtod?!? Really confusing !?!? – chrisonline Oct 13 '15 at 18:54
  • 2
    On `setAlarmClock()`: (1) When used for an alarm within the hour, it seems to prevent Doze mode. (2) It only supports wall clock time. To use it for elapsed interval time, convert to wall time and redo it upon a clock or timezone adjustment broadcast. (3) It displays an alarm clock icon on the right side of the notification bar and the lock screen showing the alarm's wall time hh:mm. This will appear buggy/sloppy if the alarm is not set on a minute boundary. (4) You give it a PendingIntent so the OS can open your Activity to edit the alarm when the user taps the icon. – Jerry101 Oct 19 '15 at 03:28
  • @Jerry101: "When used for an alarm within the hour, it seems to prevent Doze mode" -- are you implying that if the alarm is further out than that, that Doze mode blocks the event? That would be bad, but I have not tested that scenario yet. – CommonsWare Oct 19 '15 at 11:44
  • 1
    @CommonsWare - `setAlarmClock()` will wake the device up from Doze, if the next alarm is < 60min away! The behavior of `setAlarmClock()` is very interesting, see this post http://stackoverflow.com/a/33187763/4301846 – sec_aw Oct 19 '15 at 12:05
  • @CommonsWare yes, sec_aw's details agree with my tests. Apparently informing the user about an alarm clock blocks Doze mode like screen-on does. – Jerry101 Oct 19 '15 at 14:51
  • @Jerry101: I'm more worried about the "less than an hour" issue. If `setAlarmClock()` will be needed for calendaring-type apps for notifications, there can't be a limitation on how far out the alarm is. – CommonsWare Oct 19 '15 at 14:52
  • For times > 60 minutes, @sec_aw reports that the device returns from IDLE to ACTIVE mode within an hour of the alarm time. I didn't test that case yet. – Jerry101 Oct 19 '15 at 15:00
  • The doc https://developer.android.com/training/monitoring-device-state/doze-standby.html#restrictions says, _"Alarms set with `setAlarmClock()` continue to fire normally — the system exits Doze shortly before those alarms fire."_ – Jerry101 Oct 20 '15 at 18:19
  • 15
    Wow.. I can't believe calendaring / reminder apps need to use this special method which ends up showing an unwanted icon at the top - all the time. Perhaps 'Exact alerts' should turn into a Permission that users need to explicitly grant, like making direct phone calls, so that we don't have to go through this. – strangetimes Sep 16 '16 at 20:42
11

Using setExactAndAllowWhileIdle() for a one-time alarm will fire exactly on the given time even in Doze idle mode. So this probably is the way to go.

Problems start, if you want to repeat the alarm at a rate of < 15 min (or set any other at a time < 15 min away from the last one), as this will not work in Doze idle mode, where such alarms are forced to the next 15 min or are executed when idle maintenance starts, which happens for about ten minutes first after 1 hour, then after another 2 hours, then after another 4 hours and so on.

- EDIT -

As of today Nov 17, Dianne Hackborn writes in this Post's comments: "For what it's worth, the minimum time between while idle alarms will be changing to 9 minutes at some point relatively soon (even on devices running the current Marshmallow builds)."

This doesn't change anything fundamentally though.

sec_aw
  • 1,594
  • 1
  • 15
  • 26
  • Repeat the same alarm? Or repeat any alarm of the same app? – chrisonline Oct 18 '15 at 17:15
  • 1
    @chrisonline Any alarm of the same app, because it's the whole app that's being penalised. – jmart Oct 18 '15 at 17:39
  • 1
    The link to G+ post is now dead – Vadim Kotov Oct 31 '19 at 11:37
  • Also on both Android 9 and 10 I managed to have it run every 5s for more than 24hr (on Android 10 needed to whitelist for battery optimisation). Repeating the same alarm - again and again. Not setRepeat, but setExactAndAllowWhileIdle set recursively from within. – Cornelius Roemer Dec 26 '19 at 22:39
9

Here are my discussion with Ian Lake on Google+!

setExactAndAllowWhileIdle() is exact and should work. The 15 minutes time frame is wrong in the java doc.

enter image description here

chrisonline
  • 6,949
  • 11
  • 42
  • 62
  • 3
    From my test the 15 minutes limitation is still there. It's exact because the alarm is not delayed due to batching as setAndAllowWhileIdle but the docs seems OK to me. – greywolf82 Oct 14 '15 at 18:23
  • 2
    In addition: where is the bug report? I didn't find anything. – greywolf82 Oct 14 '15 at 18:24
  • If the 15 minutes limitation is still there why it is exact? What do you mean with this it will be batched as setAndAllowWhileIdle? – chrisonline Oct 14 '15 at 19:35
  • 5
    I can confirm that like in the Android M preview final, Android 6.0 factory behaves the same: setExactAndAllowWhileIdle() fires at most every 15 minutes in Doze idle, whether 'ignore optimizations' is set on or off (tested with 1 minute interval). The docs are correct. If you don't use an interval and just set setExactAndAllowWhileIdle() to fire once with RTC_WAKEUP, then it will probably be precise. There's a lot of misunderstanding with Doze mode... – sec_aw Oct 14 '15 at 23:14
  • 1
    If you want an alarm at 2:01, it works and it's exact. With the other method it's possible that the alarm is fired at 2:05. The problem is that you can't set another alarm at 2:05 but at 2:16 as minimum. – greywolf82 Oct 15 '15 at 05:22
  • 1
    Reading this again: "The only limit right now is that your app can only wake the device from doze once a minute" - I really wonder what Ian Lake is talking about exactly. It's proven several times and documented as intended behavior, that the methods ...AndAllowWhileIdle() fire at a minimum interval of 15 min in Doze idle. – sec_aw Oct 15 '15 at 13:12
  • 3
    It would be great that Ian Lake could clarify the issue. Either he's wrong or there's something he knows that we don't. – jmart Oct 15 '15 at 15:13
  • Here is the thread from the discussion with Ian Lake: https://plus.google.com/+AndroidDevelopers/posts/GdNrQciPwqo – chrisonline Oct 18 '15 at 17:19
  • After watching the 2015 Big Android BBQ talk about App Standby and Doze mode, one thing is definitely clear: the 15 minute limitation is real and is here to stay. What Ian Lake was saying wasn't correct (after all, he wasn't on the team who worked on this). Link to the talk: https://www.youtube.com/watch?v=Rwshwq_vI1s&index=23&list=PLWz5rJ2EKKc_HyE1QX9heAgTPdAMqc50z – jmart Oct 27 '15 at 09:44
  • 4
    @jmart: OK so every "Reminder" app out there can't remind a user within 15 minutes more than once!! Sorry, battery life is good, but such an bad idea. All "reminder" apps are now the bad player. If a user wants to be reminded at 2 p.m. and at 2:05 p.m. it is not possible anymore!! Great user experience!! Google why did you do this? – chrisonline Oct 27 '15 at 10:37
  • 1
    @jmart: Well, the above linked article of Ian Lake is a tutorial about how android developers should schedule their background tasks. If he doesn't know how exactly the proposed functions work then that's at least a bit unsettling. – sec_aw Oct 29 '15 at 10:04
  • @chrisonline That's what setAlarmClock() is for. It just prevents doze if alarms are within 60 mins. Still, no doze isn't that bad. It's not like you're holding a wake-lock or something. – black Oct 16 '16 at 11:02
  • @chrisonline The link to G+ is now dead – Vadim Kotov Oct 31 '19 at 11:39
  • @Vadim Yes I know because G+ doesn't exists anymore ;-) – chrisonline Nov 02 '19 at 11:10
0

I was trying to create an automation system running in the background. My frequency range was between 1-15 minutes. My wish was not to use a foreground service. By looking at the name of the method "setExactAndAllowWhileIdle", I thought that yeah it is safe to go with one-time alarms, scheduling the next one when done.

However, I couldn't find a way to run code in doze mode with alarms running more frequent than 15 minutes. Instead, I choose to start a foreground service when doze mode gets activated and stop that foreground service when phone awakes. User won't be seeing your foreground notification while using his/her phone. I don't care much about the ones in doze mode.

PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if(intent.getAction().equals("android.os.action.DEVICE_IDLE_MODE_CHANGED")){
        if (pm.isDeviceIdleMode()) {
            //startAutomationForegroundService();
        } else {
            //stopAutomationForegroundService();
            return;
        }
        AutomationReceiver.completeWakefulIntent(intent);
        return;
    }
}

You need to register "android.os.action.DEVICE_IDLE_MODE_CHANGED" intent filter into your WakefulBroadcastReceiver. Care putting it into manifest may not help.

Mertcan Çüçen
  • 466
  • 3
  • 13