setExactAndAllowWhileIdle - is not exact as of developer reference
Asked Answered
B

4

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)

Bickford answered 13/10, 2015 at 18:41 Comment(8)
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.Bleachers
Wow, Google even crippled their own app. Well done.Catwalk
Can anybody please conclude all the facts about this issue?Cytolysin
@MehulJoisar The 15 minute limitation is real and is here to stay (you can see the details on this talk: youtube.com/…). So I'd say the best answer is the one provided by CommonsWare.Bleachers
@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?Bickford
@Bickford 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.Bleachers
Yes, even many more use-cases are broken, those are not 'edge-cases' as stated in the documentation. Did you come across this thread? code.google.com/p/android-developer-preview/issues/… 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 https://mcmap.net/q/353583/-wakelock-and-doze-mode In my view the current implementation of Doze is a failed concept for many reasons.Catwalk
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.Elishaelision
T
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.

Tenement answered 13/10, 2015 at 18:51 Comment(9)
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 !?!?Bickford
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.Akilahakili
@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.Tenement
@Tenement - 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 https://mcmap.net/q/353583/-wakelock-and-doze-modeCatwalk
@Tenement yes, sec_aw's details agree with my tests. Apparently informing the user about an alarm clock blocks Doze mode like screen-on does.Akilahakili
@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.Tenement
For times > 60 minutes, @Catwalk reports that the device returns from IDLE to ACTIVE mode within an hour of the alarm time. I didn't test that case yet.Akilahakili
The doc developer.android.com/training/monitoring-device-state/… says, "Alarms set with setAlarmClock() continue to fire normally — the system exits Doze shortly before those alarms fire."Akilahakili
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.Fukuoka
C
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.

Catwalk answered 15/10, 2015 at 13:33 Comment(4)
Repeat the same alarm? Or repeat any alarm of the same app?Bickford
@Bickford Any alarm of the same app, because it's the whole app that's being penalised.Bleachers
The link to G+ post is now deadMilner
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.Monostrophe
B
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

Bickford answered 13/10, 2015 at 21:7 Comment(14)
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.Liaotung
In addition: where is the bug report? I didn't find anything.Liaotung
If the 15 minutes limitation is still there why it is exact? What do you mean with this it will be batched as setAndAllowWhileIdle?Bickford
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...Catwalk
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.Liaotung
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.Catwalk
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.Bleachers
Here is the thread from the discussion with Ian Lake: plus.google.com/+AndroidDevelopers/posts/GdNrQciPwqoBickford
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: youtube.com/…Bleachers
@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?Bickford
@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.Catwalk
@Bickford 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.Unsnap
@Bickford The link to G+ is now deadMilner
@Vadim Yes I know because G+ doesn't exists anymore ;-)Bickford
V
1

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.

Verdieverdigris answered 11/2, 2017 at 22:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.