9

In my application which was supposed to trigger an alarm at a specified time, alarm whose purpose was to inform the user via a Notification, I despaired at obtaining a non precise result. The alarm was ringing, but not at the specified time. This was systematic when the time between the setting of the alarm and the time it was supposed to go off was long. For this, I was using setExact(RTC_WAKEUP,time,intent). After many attemps to make it work, I finally saw and tried the setAlarmClock function, and everything went fine!!

According to the javadoc, setAlarmClock is identical to setExact except that it implies RTC_WAKEUP. As far as I see, at least one other difference exists. Does anyone know it?

serv-inc
  • 29,557
  • 9
  • 128
  • 146
Zelig63
  • 1,422
  • 1
  • 20
  • 35
  • The major different is `setExact` does not permit the OS to adjust the delivery time. It is delivered as nearly as possible to the requested trigger time. – Shree Krishna Mar 20 '16 at 06:25
  • That's not what the doc says : 'setExact' is supposed to replace 'set' when a precise timing is required in API above 19. From what I saw, the "as nearly as possible to the requested trigger time" could be as long as nearly 4 minutes. And two successive alarms set with a one minute interval were going off with a 5 minutes (very precisely that time) interval. – Zelig63 Mar 20 '16 at 14:30

2 Answers2

11

setExact() and setAlarmClock() both make very similar calls to setImpl(). If you use RTC_WAKEUP for your setExact() call, the only difference is the final arg of type AlarmClockInfo.

On the service side, the call is received in set(). At the end of that method, we can see the difference the extra argument makes:

@Override
public void set(int type, long triggerAtTime, long windowLength, long interval, int flags,
        PendingIntent operation, WorkSource workSource,
        AlarmManager.AlarmClockInfo alarmClock) {
    final int callingUid = Binder.getCallingUid();
    if (workSource != null) {
        getContext().enforcePermission(
                android.Manifest.permission.UPDATE_DEVICE_STATS,
                Binder.getCallingPid(), callingUid, "AlarmManager.set");
    }

    // No incoming callers can request either WAKE_FROM_IDLE or
    // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate.
    flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE
            | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);

    // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
    // manager when to come out of idle mode, which is only for DeviceIdleController.
    if (callingUid != Process.SYSTEM_UID) {
        flags &= ~AlarmManager.FLAG_IDLE_UNTIL;
    }

    // If the caller is a core system component, and not calling to do work on behalf
    // of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.  This means we
    // will allow these alarms to go off as normal even while idle, with no timing
    // restrictions.
    if (callingUid < Process.FIRST_APPLICATION_UID && workSource == null) {
        flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
    }

    // If this is an exact time alarm, then it can't be batched with other alarms.
    if (windowLength == AlarmManager.WINDOW_EXACT) {
        flags |= AlarmManager.FLAG_STANDALONE;
    }

    // If this alarm is for an alarm clock, then it must be standalone and we will
    // use it to wake early from idle if needed.
    if (alarmClock != null) {
        flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
    }

    setImpl(type, triggerAtTime, windowLength, interval, operation,
            flags, workSource, alarmClock, callingUid);
}

If there's a non-null alarmClock argument, the request gets the FLAG_WAKE_FROM_IDLE flag, on top of FLAG_STANDALONE, which you'll get in both cases. And the comment for FLAG_WAKE_FROM_IDLE says:

Flag for alarms: this alarm would like to wake the device even if it is idle. This is, for example, an alarm for an alarm clock.

If you want, you can dig into the usage of FLAG_WAKE_FROM_IDLE -- but that's the difference, as far as I can tell. Your "normal" exact alarms do not wake the device from idle, but the alarm set with setAlarmClock() does.

Snild Dolkow
  • 5,882
  • 3
  • 17
  • 31
  • You are certainly right. The explanation should come from this 'FLAG_WAKE_FROM_IDLE' flag. The only thing I regret is that the javaDoc is misleading (it says that if you want your alarm to be precise, use 'setExact' which is not correct) and this flag doesn't appear in the javadoc. – Zelig63 Mar 21 '16 at 16:23
  • Yes, it appears that `FLAG_WAKE_FROM_IDLE` is internal to AlarmManager. It looks like it's related to the "Doze mode" feature introduced in Android Marshmallow. To be fair, setExact() docs say "The alarm will be delivered as nearly as possible to the requested trigger time". I guess the definition of "as nearly as possible" could be bent to include "I'd prefer to save battery right now"... – Snild Dolkow Mar 21 '16 at 16:32
  • I agree with you that the statement you propose would be closer to the reality and much less misleading. – Zelig63 Mar 22 '16 at 16:02
  • 1
    https://developer.android.com/training/monitoring-device-state/doze-standby.html explains that Alarms set with setAlarmClock() continue to fire normally even in Doze mode, not deferred to the next maintenance window. The system exits Doze shortly before those alarms fire. Too bad the JavaDoc doesn't link to this. It does say that setAlarmClock() displays information about this alarm to the user. – Jerry101 Aug 05 '16 at 21:45
7

Doze mode, introduced in Android 6.0 (API level 23), adds further restrictions to those outlined above: (TL;DR: only setAlarmClock triggers in doze mode, and adds a system icon)

Doze restrictions

The following restrictions apply to your apps while in Doze: [...]

  • The system ignores wake locks.
  • Standard AlarmManager alarms (including setExact() and setWindow()) are deferred to the next maintenance window.
    • 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. [...]

The devices enter the low-power doze-state with periodic wake-ups to perform the other alarms etc.

serv-inc
  • 29,557
  • 9
  • 128
  • 146