Switch network cycle calculation to use Calendar.

The older Time class is deprecated and doesn't handle edge cases
very well.  So migrate the cycle calculation logic to use the
long-standing and well-supported Calendar API.

Bug: 28689087
Change-Id: Ic1802b3f8556402f99bfea4cd625c35dfed81ac0
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 51c45e0..11b861a 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -18,7 +18,6 @@
 
 import static android.content.pm.PackageManager.GET_SIGNATURES;
 import static android.net.NetworkPolicy.CYCLE_NONE;
-import static android.text.format.Time.MONTH_DAY;
 
 import android.content.Context;
 import android.content.Intent;
@@ -27,12 +26,13 @@
 import android.content.pm.Signature;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.text.format.Time;
 import android.util.DebugUtils;
 
 import com.google.android.collect.Sets;
 
+import java.util.Calendar;
 import java.util.HashSet;
+import java.util.TimeZone;
 
 /**
  * Manager for creating and modifying network policy rules.
@@ -253,28 +253,18 @@
             throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
         }
 
-        final Time now = new Time(policy.cycleTimezone);
-        now.set(currentTime);
+        final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone));
+        cal.setTimeInMillis(currentTime);
+        snapToCycleDay(cal, policy.cycleDay);
 
-        // first, find cycle boundary for current month
-        final Time cycle = new Time(now);
-        cycle.hour = cycle.minute = cycle.second = 0;
-        snapToCycleDay(cycle, policy.cycleDay);
-
-        if (Time.compare(cycle, now) >= 0) {
-            // cycle boundary is beyond now, use last cycle boundary; start by
-            // pushing ourselves squarely into last month.
-            final Time lastMonth = new Time(now);
-            lastMonth.hour = lastMonth.minute = lastMonth.second = 0;
-            lastMonth.monthDay = 1;
-            lastMonth.month -= 1;
-            lastMonth.normalize(true);
-
-            cycle.set(lastMonth);
-            snapToCycleDay(cycle, policy.cycleDay);
+        if (cal.getTimeInMillis() >= currentTime) {
+            // Cycle boundary is beyond now, use last cycle boundary
+            cal.set(Calendar.DAY_OF_MONTH, 1);
+            cal.add(Calendar.MONTH, -1);
+            snapToCycleDay(cal, policy.cycleDay);
         }
 
-        return cycle.toMillis(true);
+        return cal.getTimeInMillis();
     }
 
     /** {@hide} */
@@ -283,28 +273,18 @@
             throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
         }
 
-        final Time now = new Time(policy.cycleTimezone);
-        now.set(currentTime);
+        final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone));
+        cal.setTimeInMillis(currentTime);
+        snapToCycleDay(cal, policy.cycleDay);
 
-        // first, find cycle boundary for current month
-        final Time cycle = new Time(now);
-        cycle.hour = cycle.minute = cycle.second = 0;
-        snapToCycleDay(cycle, policy.cycleDay);
-
-        if (Time.compare(cycle, now) <= 0) {
-            // cycle boundary is before now, use next cycle boundary; start by
-            // pushing ourselves squarely into next month.
-            final Time nextMonth = new Time(now);
-            nextMonth.hour = nextMonth.minute = nextMonth.second = 0;
-            nextMonth.monthDay = 1;
-            nextMonth.month += 1;
-            nextMonth.normalize(true);
-
-            cycle.set(nextMonth);
-            snapToCycleDay(cycle, policy.cycleDay);
+        if (cal.getTimeInMillis() <= currentTime) {
+            // Cycle boundary is before now, use next cycle boundary
+            cal.set(Calendar.DAY_OF_MONTH, 1);
+            cal.add(Calendar.MONTH, 1);
+            snapToCycleDay(cal, policy.cycleDay);
         }
 
-        return cycle.toMillis(true);
+        return cal.getTimeInMillis();
     }
 
     /**
@@ -313,16 +293,17 @@
      *
      * @hide
      */
-    public static void snapToCycleDay(Time time, int cycleDay) {
-        if (cycleDay > time.getActualMaximum(MONTH_DAY)) {
-            // cycle day isn't valid this month; snap to last second of month
-            time.month += 1;
-            time.monthDay = 1;
-            time.second = -1;
+    public static void snapToCycleDay(Calendar cal, int cycleDay) {
+        cal.set(Calendar.HOUR_OF_DAY, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
+        if (cycleDay > cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
+            cal.set(Calendar.DAY_OF_MONTH, 1);
+            cal.add(Calendar.MONTH, 1);
+            cal.add(Calendar.SECOND, -1);
         } else {
-            time.monthDay = cycleDay;
+            cal.set(Calendar.DAY_OF_MONTH, cycleDay);
         }
-        time.normalize(true);
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 622e46e..979f160 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -20,6 +20,7 @@
 import static android.content.Intent.EXTRA_UID;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkPolicy.CYCLE_NONE;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
@@ -86,7 +87,9 @@
 import org.easymock.IAnswer;
 
 import java.io.File;
+import java.util.Calendar;
 import java.util.LinkedHashSet;
+import java.util.TimeZone;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -141,8 +144,7 @@
     private static final int PID_2 = 401;
     private static final int PID_3 = 402;
 
-    @Override
-    public void setUp() throws Exception {
+    public void _setUp() throws Exception {
         super.setUp();
 
         setCurrentTimeMillis(TEST_START);
@@ -229,8 +231,7 @@
 
     }
 
-    @Override
-    public void tearDown() throws Exception {
+    public void _tearDown() throws Exception {
         for (File file : mPolicyDir.listFiles()) {
             file.delete();
         }
@@ -263,6 +264,7 @@
         backgroundChanged.get();
     }
 
+    @Suppress
     public void testPidForegroundCombined() throws Exception {
         IdleFuture idle;
 
@@ -310,6 +312,7 @@
         assertFalse(mService.isUidForeground(UID_A));
     }
 
+    @Suppress
     public void testScreenChangesRules() throws Exception {
         Future<Void> future;
 
@@ -351,6 +354,7 @@
         verifyAndReset();
     }
 
+    @Suppress
     public void testPolicyNone() throws Exception {
         Future<Void> future;
 
@@ -381,6 +385,7 @@
         verifyAndReset();
     }
 
+    @Suppress
     public void testPolicyReject() throws Exception {
         Future<Void> future;
 
@@ -412,6 +417,7 @@
         verifyAndReset();
     }
 
+    @Suppress
     public void testPolicyRejectAddRemove() throws Exception {
         Future<Void> future;
 
@@ -576,6 +582,17 @@
                 computeLastCycleBoundary(parseTime("2013-01-14T15:11:00.000-08:00"), policy));
     }
 
+    public void testLastCycleBoundaryDST() throws Exception {
+        final long currentTime = parseTime("1989-01-02T07:30:00.000");
+        final long expectedCycle = parseTime("1988-12-03T02:00:00.000Z");
+
+        final NetworkPolicy policy = new NetworkPolicy(
+                sTemplateWifi, 3, "America/Argentina/Buenos_Aires", 1024L, 1024L, false);
+        final long actualCycle = computeLastCycleBoundary(currentTime, policy);
+        assertTimeEquals(expectedCycle, actualCycle);
+    }
+
+    @Suppress
     public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
         NetworkState[] state = null;
         NetworkStats stats = null;
@@ -628,6 +645,7 @@
         verifyAndReset();
     }
 
+    @Suppress
     public void testUidRemovedPolicyCleared() throws Exception {
         Future<Void> future;
 
@@ -652,6 +670,7 @@
         verifyAndReset();
     }
 
+    @Suppress
     public void testOverWarningLimitNotification() throws Exception {
         NetworkState[] state = null;
         NetworkStats stats = null;
@@ -783,6 +802,7 @@
         }
     }
 
+    @Suppress
     public void testMeteredNetworkWithoutLimit() throws Exception {
         NetworkState[] state = null;
         NetworkStats stats = null;