Alarm Manager issue in Android 6.0 Doze mode

FilipeOS picture FilipeOS · Feb 25, 2016 · Viewed 9.3k times · Source

I've made an app that always worked until Android 6.0. I think it's the Doze feature that it's not allowing my Alarm to fire.

I use sharedpreferences to handle the options:

//ENABLE NIGHT MODE TIMER
    int sHour = blockerTimerPreferences.getInt("sHour", 00);
    int sMinute = blockerTimerPreferences.getInt("sMinute", 00);

    Calendar sTime = Calendar.getInstance();
    sTime.set(Calendar.HOUR_OF_DAY, sHour);
    sTime.set(Calendar.MINUTE, sMinute);

    Intent enableTimer = new Intent(context, CallReceiver.class);
    enableTimer.putExtra("activate", true);
    PendingIntent startingTimer = PendingIntent.getBroadcast(context, 11002233, enableTimer, PendingIntent.FLAG_UPDATE_CURRENT);
    AlarmManager sAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    sAlarm.setRepeating(AlarmManager.RTC_WAKEUP,
            sTime.getTimeInMillis(),
            AlarmManager.INTERVAL_DAY, startingTimer);

Any clue of whats wrong here?

This is an app to block calls. Thank you!

EDIT: I have 3 files (more but...) like:

MainActivity (All code)
CallReceiver (Broadcast that triggers the alarm again (reboot etc))
CallReceiverService (Handles the call / phone state)

Answer

xiaomi picture xiaomi · Feb 26, 2016

The Doze mode will delay your alarm until the next maintenance window. To avoid the Doze mode to block your alarm, you can use setAndAllowWhileIdle(), setExactAndAllowWhileIdle() or setAlarmClock(). You will have about 10s to execute your code, and set your next alarm (not more than once per 15min for methods with _AndAllowWhileIdle though)

If you want to test the Doze mode, you can use ADB command:

  1. Configure a hardware device or virtual device with an Android 6.0 (API level 23) or higher system image.

  2. Connect the device to your development machine and install your app.

  3. Run your app and leave it active.
  4. Shut off the device screen. (The app remains active.) Force the system to cycle through Doze modes by running the following commands:

    adb shell dumpsys battery unplug

    adb shell dumpsys deviceidle step

  5. You may need to run the second command more than once. Repeat it until the device state changes to idle.

  6. Observe the behavior of your app after you reactivate the device. Make sure the app recovers gracefully when the device exits Doze.

Edit: Add setAlarmClock example

Don't forget to check the SDK level (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)

AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent intent = new Intent(this, MyAlarmReceiver.class); //or just new Intent() for implicit intent 
//set action to know this come from the alarm clock
intent.setAction("from.alarm.clock");
PendingIntent pi = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//Alarm fire in 5s.
am.setAlarmClock(new AlarmManager.AlarmClockInfo(System.currentTimeMillis() + 5000, pi), pi);