I have a Nexus S, and when I change the date manually on the phone, ACTION_DATE_CHANGED is not always broadcasted. If I change the date from Feb 13, 2014 to Feb 14, 2014, I have not gotten an ACTION_DATE_CHANGED to work, but if I set it to several years in the future, I sometimes get it to fire.
I can (99%) assure you I'm not misusing IntentFilters, BroadcastReceivers, etc. I'm just curious as to why this broadcast is so poorly documented. A quick scan through SO & Google shows that people aren't sure if it happens when the user manually changes it, or when the date rolls over at 12:00am every day, or both. My experience shows that it's pretty inconsistent regarding user changes and I haven't tried system changes.
I'll grep through the AOSP code and isolate all of the points where this is fired and report back.
Edit: the question: Anyone have any idea what's going on here? :-)
Here is code from 4.0.3_r1 in frameworks/base/services/java/android/server/AlarmManagerService.java.
First, we create a PendingIntent mDateChangeSender;
private final PendingIntent mDateChangeSender;
Then, in the constructor of AlarmManagerService.java, we setup the PendingIntent:
Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0);
Then later in the constructor:
mClockReceiver.scheduleDateChangedEvent();
So what is mClockReceiver? Just a BroadcastReceiver listening for Intent.ACTION_TIME_TICK and Intent.ACTION_DATE_CHANGED. In it's onReceive():
...
else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
...
scheduleDateChangedEvent();
}
Then, later we find the method scheduleDateChangedEvent():
public void scheduleDateChangedEvent() {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.add(Calendar.DAY_OF_MONTH, 1);
set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
}
So it sets a one-shot alarm, starting with the current time, then setting hour/min/sec/milli to zero, then adding a day, so if it was 1:30pm today, the next time it will get fired would be in 10 hours and 30 minutes.
This isn't to say there aren't bugs or anything here, but it LOOKS like ACTION_DATE_CHANGED should fire at midnight every day.
NOW - if I were to change the date on the phone lets say 10 years into the future. The code to handle the change in time will fire the first ACTION_DATE_CHANGED event then schedule a new ACTION_DATE_CHANGED to get fired, at 10 years + some fraction of a day. Then if we change the date back 10 years, to the correct date, the alarm is still scheduled to be fired in 10 years, thuse ACTION_DATE_CHANGED will no longer get fired (unless you set the date further than 10 years from now - try it!).
tl;dr: This is a bug in Android.
Matching ZachM's research, https://code.google.com/p/android/issues/detail?id=2880 logs an AOSP bug where after the clock get set backwards, ACTION_DATE_CHANGED
won't fire again until the clock catches up with what was going to be the next day.
(There's also a note there about multi-hour delays in this broadcast. A consequence of setting the clock for testing or of the date changing while the device is sleeping?)
Outside of the bug, ACTION_DATE_CHANGED
indicates that the clock reached the next day, while ACTION_TIME_CHANGED
(== "android.intent.action.TIME_SET"
) indicates that the clock was set (adjusted).
ZachM's "If I were to patch this..." sounds like a fine workaround, using such an alarm within an app (not broadcast).
EDIT
AOSP bug #2880 has a new comment pointing out a (another?) bug in scheduleDateChangedEvent()
: Where it calls calendar.set(Calendar.HOUR, 0);
, the first parameter ought to be HOUR_OF_DAY
.
From java.util.Calendar:
HOUR is used for the 12-hour clock (0 - 11). Noon and midnight are represented by 0, not by 12. E.g., at 10:04:15.250 PM the HOUR is 10.
HOUR_OF_DAY is used for the 24-hour clock. E.g., at 10:04:15.250 PM the HOUR_OF_DAY is 22.
So if scheduleDateChangedEvent()
runs during the PM half of the day, it will schedule the next alarm for noon rather than midnight.
I also encountered the same issue. I noticed that even when using ACTION_TIME_CHANGED
and ACTION_DATE_CHANGED
together, I wasn't able to detect always when date changed. I think it's a bug in platform.
So I added receiver for ACTION_TIME_TICK
and check in that callback when the date changed.
You actually can use BroadcastReceiver. Just use ACTION_TIMEZONE_CHANGED too.
I've made a nice solution for this, here:
https://stackoverflow.com/a/48782963/878126