Android ACTION_DATE_CHANGED broadcast

ZachM picture ZachM · Feb 13, 2014 · Viewed 12.3k times · Source

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? :-)

Answer

ZachM picture ZachM · Feb 13, 2014

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.