16 January

Update / Solution for broken Android calendar syncing

In my last post, I described how Android’s calendar syncing was broken for me. I noticed that my calendar on my phone was out of date, and when I manually refreshed, I’d get a force-close error.

After downloading the Android source, figuring out how to build, and playing with it on the emulator and my device for some time, I have figured out what the problem is, and have a work-around for it. Essentially some repeated events can have a start-date Android is unhappy with (I believe it’s due to a start time of UTC 0). This causes an Android core library to throw an TimeFormatException which is never properly handled, preempting syncing. This is a pretty big bug — that exception should be caught by Google’s common calendar code, but the exception is ignored. (This is because of the misuse of unchecked exceptions — android.util.TimeFormatException inherits from RuntimeException for no good reason at all that I can see. Checked exceptions are one of the best features of Java, and inheriting from RuntimeException for things that should be handled is a really bad idea, IMO.).

Here is the text of the item that was breaking my calendar syncing:

<gd:recurrence>DTSTART;TZID=GMT+05:30:20120104T210000
DTEND;TZID=GMT+05:30:20120104T220000
RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20121231T182959Z
BEGIN:VTIMEZONE
TZID:GMT+05:30
X-LIC-LOCATION:GMT+05:30
BEGIN:STANDARD
TZOFFSETFROM:+0530
TZOFFSETTO:+0530
TZNAME:IST
DTSTART:19700101T000000
END:STANDARD
END:VTIMEZONE
</gd:recurrence>

This was in the private url for my feed. You can see yours here:
https://www.google.com/calendar/feeds/USER_NAME%40gmail.com/private/full. I think this event was added by Outlook somehow, but I’m not really sure. The web UI and other clients have no problem dealing with this event, but Android’s date parser is unhappy with it. If you’re seeing repeated calendar syncing crashes, go to the above url, replace USER_NAME with your user id, and see if you have something similar to this string. If so, deleting that event ought to fix syncing.


How Google should fix this

If someone on Android or Calendar is reading this, there are two ways this should be fixed. Please do both of them!

  1. Fix Android to handle these errors gracefully. I patched the provider code to fix this bug. Someone should fix this, and include it in the next ICS update. Here’s the diff:

    vijayp@thecoon:/mnt/largelinux/bigfiles/as2/frameworks/opt/calendar/src/com/android/calendarcommon$ git diff -w
    diff --git a/src/com/android/calendarcommon/RecurrenceSet.java b/src/com/android/calendarcommon/RecurrenceSet.java
    index 3b91a1d..8e1117e 100644
    --- a/src/com/android/calendarcommon/RecurrenceSet.java
    +++ b/src/com/android/calendarcommon/RecurrenceSet.java
    @@ -178,6 +178,7 @@ public class RecurrenceSet {
    */
    public static boolean populateContentValues(ICalendar.Component component,
    ContentValues values) {
    + try {
    ICalendar.Property dtstartProperty =
    component.getFirstProperty("DTSTART");
    String dtstart = dtstartProperty.getValue();
    @@ -233,6 +234,11 @@ public class RecurrenceSet {
    values.put(CalendarContract.Events.DURATION, duration);
    values.put(CalendarContract.Events.ALL_DAY, allDay ? 1 : 0);
    return true;
    + } catch (TimeFormatException e) {
    + // This happens when the data is out of range.
    + Log.i(TAG, "BAD data: " + component.toString());
    + return false;
    + }
    }

  2. Patch the calendar FE server to remove things that break android. Fixing Android is the correct solution because it’s unclear that the data it is passing are actually bad. But since the Calendar Frontend can be fixed in a few days, and it might take months (or years!) to get carriers to agree to roll out an Android update, it’s best to just patch the Calendar FE to filter out data that might cause Android to crash. It can even be enabled based on the useragent.

Anyway, I really hope someone at Google reads and fixes this. I spent a lot of unnecessary time tracking this down!

14 January

Android calendar syncing is broken for me!

Posted by in code, software | One Comment

For the past couple of weeks, (shortly after my nexus s upgraded itself to ICS), the calendar on my phone has not been syncing with Google. This has required me to use the calendar website on my phone, which is not a pleasant experience at all. So today, I hooked my phone up to my computer and decided to do some debugging. Using adb logcat, I found this stack trace:

E/AndroidRuntime(15353): FATAL EXCEPTION: SyncAdapterThread-2
E/AndroidRuntime(15353): android.util.TimeFormatException: Parse error at pos=2
E/AndroidRuntime(15353): at android.text.format.Time.nativeParse(Native Method)
E/AndroidRuntime(15353): at android.text.format.Time.parse(Time.java:440)
E/AndroidRuntime(15353): at com.android.calendarcommon.RecurrenceSet.populateContentValues(RecurrenceSet.java:189)
E/AndroidRuntime(15353): at com.google.android.syncadapters.calendar.EventHandler.entryToContentValues(EventHandler.java:1138)
E/AndroidRuntime(15353): at com.google.android.syncadapters.calendar.EventHandler.applyEntryToEntity(EventHandler.java:616)
E/AndroidRuntime(15353): at com.google.android.syncadapters.calendar.CalendarSyncAdapter.getServerDiffsImpl(CalendarSyncAdapter.java:2223)
E/AndroidRuntime(15353): at com.google.android.syncadapters.calendar.CalendarSyncAdapter.getServerDiffsForFeed(CalendarSyncAdapter.java:1954)
E/AndroidRuntime(15353): at com.google.android.syncadapters.calendar.CalendarSyncAdapter.getServerDiffsOrig(CalendarSyncAdapter.java:945)
E/AndroidRuntime(15353): at com.google.android.syncadapters.calendar.CalendarSyncAdapter.innerPerformSync(CalendarSyncAdapter.java:417)
E/AndroidRuntime(15353): at com.google.android.syncadapters.calendar.CalendarSyncAdapter.onPerformLoggedSync(CalendarSyncAdapter.java:302)
E/AndroidRuntime(15353): at com.google.android.common.LoggingThreadedSyncAdapter.onPerformSync(LoggingThreadedSyncAdapter.java:33)
E/AndroidRuntime(15353): at android.content.AbstractThreadedSyncAdapter$SyncThread.run(AbstractThreadedSyncAdapter.java:247)
W/ActivityManager( 153): Force finishing activity com.google.android.calendar/com.android.calendar.AllInOneActivity
V/CalendarSyncAdapter(15353): GDataFeedFetcher thread ended: mForcedClosed is true

Thanks to Evan I was able to clone the git repo for the Calendar app (https://android.googlesource.com/platform/packages/apps/Calendar.git) , and spent some time today trying to track down this bug.

Unfortunately, the buggy code is in calendarcommon, which isn’t included as part of the git file, and is actually nearly impossible to find. At any rate, with some more digging, the closest I could get is the code here

http://git.insignal.co.kr/?p=mirror/aosp/platform/frameworks/opt/calendar.git;a=blob;f=src/com/android/calendarcommon/RecurrenceSet.java

I think there needs to be a try/except block for that whole method (around line 189) that returns false if an exception is thrown. For some reason that TimeFormatException is derived from RuntimeError (!!). The common code doesn’t seem to be installed as part of the calendar app. From quickly looking at the code, It appears as if it is installed as part of the os and registers itself as the handler for calendar uris.

So if I wanted to fix this myself, I’m wonder whether I would have to fork the code above, and install it as a new handler, then somehow hide the one with the OS? I have to think about this a bit more. The other problem is that since this is a common library, many other calendar apps might suffer from the same exception when they attempt to sync.

In the meantime, I’m going to try to figure out what event is causing this error (not easy since there are no logs that can help me) and/or think of buying an iPhone.

If you know anyone on Android who could help with this, please let me know.

Edit:
I’m downloading the entire android source code, and I think I’m going to try to re-build a patched version of the common code, uninstall the existing common code, and push the new one over it. I’ll update this post with progress …