DO NOT MERGE: Fix serious DST issue with non-DST time zones

* When finding a match for an EAS time zone without DST, we were
  checking the offset, but NOT the DST status; therefore, we could
  match the wrong time zone (depending on the order of items in the
  TZ database)
* All users with events set up in non-DST timezones would have their
  events show up at the wrong time after a DST transition
* The fix is to first check against the default time zone, and use
  that if it's a match; otherwise, to use the first time zone that
  matches both offset and DST availability

Bug: 4337360
Change-Id: Ia26590972c1b0eab4640a3082881a28ee0a81622
diff --git a/src/com/android/exchange/utility/CalendarUtilities.java b/src/com/android/exchange/utility/CalendarUtilities.java
index 7dd24e0..40848f8 100644
--- a/src/com/android/exchange/utility/CalendarUtilities.java
+++ b/src/com/android/exchange/utility/CalendarUtilities.java
@@ -30,14 +30,15 @@
 import com.android.exchange.SyncManager;
 import com.android.exchange.adapter.Serializer;
 import com.android.exchange.adapter.Tags;
+import com.android.internal.util.ArrayUtils;
 
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Entity;
-import android.content.EntityIterator;
 import android.content.Entity.NamedContentValues;
+import android.content.EntityIterator;
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.RemoteException;
@@ -698,7 +699,7 @@
         TimeZone timeZone = sTimeZoneCache.get(timeZoneString);
         if (timeZone != null) {
             if (Eas.USER_LOG) {
-                SyncManager.log(TAG, " Using cached TimeZone " + timeZone.getDisplayName());
+                SyncManager.log(TAG, " Using cached TimeZone " + timeZone.getID());
             }
         } else {
             timeZone = tziStringToTimeZoneImpl(timeZoneString);
@@ -739,14 +740,31 @@
             TimeZoneDate dstEnd =
                 getTimeZoneDateFromSystemTime(timeZoneBytes, MSFT_TIME_ZONE_STANDARD_DATE_OFFSET);
             if (dstEnd == null) {
-                // In this case, there is no daylight savings time, so the only interesting data
-                // is the offset, and we know that all of the zoneId's match; we'll take the first
-                timeZone = TimeZone.getTimeZone(zoneIds[0]);
-                if (Eas.USER_LOG) {
-                    SyncManager.log(TAG, "TimeZone without DST found by offset: " +
-                            timeZone.getDisplayName());
+                // If the default time zone is a match
+                TimeZone defaultTimeZone = TimeZone.getDefault();
+                if (!defaultTimeZone.useDaylightTime() &&
+                        ArrayUtils.contains(zoneIds, defaultTimeZone.getID())) {
+                    if (Eas.USER_LOG) {
+                        SyncManager.log(TAG, "TimeZone without DST found to be default: " +
+                                defaultTimeZone.getID());
+                    }
+                    return defaultTimeZone;
                 }
-                return timeZone;
+                // In this case, there is no daylight savings time, so the only interesting data
+                // for possible matches is the offset and DST availability; we'll take the first
+                // match for those
+                for (String zoneId: zoneIds) {
+                    timeZone = TimeZone.getTimeZone(zoneId);
+                    if (!timeZone.useDaylightTime()) {
+                        if (Eas.USER_LOG) {
+                            SyncManager.log(TAG, "TimeZone without DST found by offset: " +
+                                    timeZone.getID());
+                        }
+                        return timeZone;
+                    }
+                }
+                // None found, return null
+                return null;
             } else {
                 TimeZoneDate dstStart = getTimeZoneDateFromSystemTime(timeZoneBytes,
                         MSFT_TIME_ZONE_DAYLIGHT_DATE_OFFSET);
@@ -791,7 +809,7 @@
                 timeZone = TimeZone.getTimeZone(zoneIds[0]);
                 if (Eas.USER_LOG) {
                     SyncManager.log(TAG, "No TimeZone with correct DST settings; using first: " +
-                            timeZone.getDisplayName());
+                            timeZone.getID());
                 }
                 return timeZone;
             }
diff --git a/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java b/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java
index 65443fa..43da5f0 100644
--- a/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java
+++ b/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java
@@ -91,6 +91,12 @@
         "AAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAACgARwBNAFQAKwAwADAAOgAwADAAKQAgAFQAaQBtAGUAIABaAG8A" +
         "bgBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAEAAAAAAAAAxP///w==";
 
+    // This time zone has no DST, but earlier, buggy code retrieved a TZ WITH DST
+    private static final String ARIZONA_TIME =
+        "pAEAAFUAUwAgAE0AbwB1AG4AdABhAGkAbgAgAFMAdABhAG4AZABhAHIAZAAgAFQAaQBtAGUAAAAAAAAAAAAA" +
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFUAUwAgAE0AbwB1AG4AdABhAGkAbgAgAEQAYQB5AGwAaQBnAGgA" +
+        "dAAgAFQAaQBtAGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
+
     private static final String ORGANIZER = "organizer@server.com";
     private static final String ATTENDEE = "attendee@server.com";
 
@@ -125,6 +131,9 @@
         tz = CalendarUtilities.tziStringToTimeZone(GMT_UNKNOWN_DAYLIGHT_TIME);
         int bias = tz.getOffset(System.currentTimeMillis());
         assertEquals(0, bias);
+        // Make sure non-DST TZ's work properly
+        tz = CalendarUtilities.tziStringToTimeZone(ARIZONA_TIME);
+        assertEquals("America/Phoenix", tz.getID());
     }
 
     public void testGenerateEasDayOfWeek() {