Search code examples
androidalarmmanagersleepdozesetalarmclock

setAlarmClock() fires too late in doze mode


I have soooo much trouble getting my radio alarm clock working as intended and I have read a lot of threads here about that topic, but unfortunatley none did help me.

AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);        
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent penInt = PendingIntent.getBroadcast(this, intentId, intent, 0);

This method of distinction between the API levels I found here on stackoverflow and put it inside my calcNextAlarm() function (plus some log messages for debugging) to set the alarm correctly no matter what API is being used on the device:

// problems in doze mode api 23+
if (Build.VERSION.SDK_INT >= 23) {
    if (testMode) Log.d("Ben", "setAlarmClock() - API 23+");
    am.setAlarmClock(new AlarmManager.AlarmClockInfo(alarmTimeInMillis, penInt), penInt);
}

else if (Build.VERSION.SDK_INT >= 19) {
// Wakes up the device in Idle Mode
    if (testMode) Log.d("Ben", "setExact() - API >= 19 && API < 23");
    am.setExact(AlarmManager.RTC_WAKEUP, alarmTimeInMillis, penInt);
}
// Old APIs
else {
    if (testMode) Log.d("Ben", "set() - API < 19");
    am.set(AlarmManager.RTC_WAKEUP, alarmTimeInMillis, penInt);
}

According to the Log.d messages I can see that, on my Android 7.1 Device the first method setAlarmClock() is being executed to set the alarm in the Receiver.

I am getting really desperate after 3 weeks of unsuccessful tests and coding - my alarm today got off 4 minutes too late again - this should never happen, according to the doze mode training page:

Alarms set with setAlarmClock() continue to fire normally — the system exits Doze shortly before those alarms fire.

On my 7.1 phone the alarm will even be 20 seconds to 1:40 mins late when I set the alarm to "now +5 or 6" minutes. Could anyone advise me how to really ALWAYS get the alarm off perfectly in time?


Solution

  • Try using:

    setExactAndAllowWhileIdle()
    

    This will ensure that the alarm fires on time. I have tested it in my own app and it's reliable.

    If you are targeting API less than 23, keep this inside an if clause which checks the current api installed in the device. Use this only above API level 23, for rest keep using setExact

    All right to answer your comment:

    1) i have accurately tested it in doze mode without the battery being plugged in

    2) yea unfortunately there's the one alarm per 9 minutes limit for while idle type alarms. You have two alternatives here:

    First use the setExactAndAllowWhileIdle to fire the first alarm.

    a) For snooze, you will have to use setAlarmClock method

    b) for snooze, you can have a JobScheduler job scheduled with minimum latency time set as your snooze time. This will ensure that the job gets at least scheduled at the gap of the snooze interval. But just this will cause the job to get randomly scheduled after the interval so you'll also need to have an override deadline set to 0 so that the job gets scheduled immediately after the minimum latency. Also keep network requirements as none and idle mode needed as default or false. This is also as reliable as the alarm exact methods as per my experience and i personally use this approach.