Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 552
Ben Avatar asked Dec 28 '17 14:12

Ben


2 Answers

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.

like image 147
Kushan Avatar answered Dec 07 '22 02:12

Kushan


The documentation states that:

  • 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.

In "Adapt your app to Doze" section it also says that:

Note: Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 9 minutes, per app.

The documentation for setExactAndAllowWhileIdle says that:

Unlike other alarms, the system is free to reschedule this type of alarm to happen out of order with any other alarms, even those from the same app. This will clearly happen when the device is idle (since this alarm can go off while idle, when any other alarms from the app will be held until later), but may also happen even when not idle. Note that the OS will allow itself more flexibility for scheduling these alarms than regular exact alarms, since the application has opted into this behavior. When the device is idle it may take even more liberties with scheduling in order to optimize for battery life.

So setExactAndAllowWhileIdle is not guaranteeing the exact execution, and setAlarmClock is more exact (according to documentation).

like image 21
badadin Avatar answered Dec 07 '22 00:12

badadin