Fix movement of RTC alarms with time changes

Whenever the system time changes, we re-add all the alarms but the
re-adding logic was not resetting the expectedWhenElapsed fields,
resulting in wrong assignment of the final whenElapsed upon app-standby
adjustment evaluation which assumes expectedWhenElapsed >= whenElapsed.

Also, whenever app-standby adjusted delivery of a repeating alarm, it
was incorrectly scheduling its subsequent occurrence.

Test: atest com.android.server.AlarmManagerServiceTest
atest CtsAlarmManagerTestCases:TimeChangeTests

Bug: 120187902
Bug: 110910377
Change-Id: I4bdb72ae55bad45ef666f7caa5d125e8278d85fd
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 72899f6..cb61259 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1138,8 +1138,8 @@
                     ? clampPositive(whenElapsed + a.windowLength)
                     : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
         }
-        a.whenElapsed = whenElapsed;
-        a.maxWhenElapsed = maxElapsed;
+        a.expectedWhenElapsed = a.whenElapsed = whenElapsed;
+        a.expectedMaxWhenElapsed = a.maxWhenElapsed = maxElapsed;
         setImplLocked(a, true, doValidate);
     }
 
@@ -1243,7 +1243,7 @@
                 alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
                 // Also schedule its next recurrence
                 final long delta = alarm.count * alarm.repeatInterval;
-                final long nextElapsed = alarm.whenElapsed + delta;
+                final long nextElapsed = alarm.expectedWhenElapsed + delta;
                 setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
                         maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
                         alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true,
@@ -3596,10 +3596,9 @@
                     // this adjustment will be zero if we're late by
                     // less than one full repeat interval
                     alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
-
                     // Also schedule its next recurrence
                     final long delta = alarm.count * alarm.repeatInterval;
-                    final long nextElapsed = alarm.whenElapsed + delta;
+                    final long nextElapsed = alarm.expectedWhenElapsed + delta;
                     setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
                             maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
                             alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true,
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 6517303..77e2517 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -43,6 +43,8 @@
 import static com.android.server.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
 import static com.android.server.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
 import static com.android.server.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
+import static com.android.server.AlarmManagerService.IS_WAKEUP_MASK;
+import static com.android.server.AlarmManagerService.TIME_CHANGED_MASK;
 import static com.android.server.AlarmManagerService.WORKING_INDEX;
 
 import static org.junit.Assert.assertEquals;
@@ -126,13 +128,15 @@
     private MockitoSession mMockingSession;
     private Injector mInjector;
     private volatile long mNowElapsedTest;
+    private volatile long mNowRtcTest;
     @GuardedBy("mTestTimer")
     private TestTimer mTestTimer = new TestTimer();
 
     static class TestTimer {
         private long mElapsed;
         boolean mExpired;
-        int mType;
+        private int mType;
+        private int mFlags; // Flags used to decide what needs to be evaluated.
 
         synchronized long getElapsed() {
             return mElapsed;
@@ -147,7 +151,16 @@
             return mType;
         }
 
+        synchronized int getFlags() {
+            return mFlags;
+        }
+
         synchronized void expire() throws InterruptedException {
+            expire(IS_WAKEUP_MASK); // Default: evaluate eligibility of all alarms
+        }
+
+        synchronized void expire(int flags) throws InterruptedException {
+            mFlags = flags;
             mExpired = true;
             notifyAll();
             // Now wait for the alarm thread to finish execution.
@@ -181,7 +194,7 @@
                 }
                 mTestTimer.mExpired = false;
             }
-            return AlarmManagerService.IS_WAKEUP_MASK; // Doesn't matter, just evaluate.
+            return mTestTimer.getFlags();
         }
 
         @Override
@@ -215,6 +228,11 @@
         }
 
         @Override
+        long getCurrentTimeMillis() {
+            return mNowRtcTest;
+        }
+
+        @Override
         AlarmManagerService.ClockReceiver getClockReceiver(AlarmManagerService service) {
             return mClockReceiver;
         }
@@ -340,7 +358,7 @@
     }
 
     @Test
-    public void testSingleAlarmSet() {
+    public void singleElapsedAlarmSet() {
         final long triggerTime = mNowElapsedTest + 5000;
         final PendingIntent alarmPi = getNewMockPendingIntent();
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
@@ -348,6 +366,33 @@
     }
 
     @Test
+    public void singleRtcAlarmSet() {
+        mNowElapsedTest = 54;
+        mNowRtcTest = 1243;     // arbitrary values of time
+        final long triggerRtc = mNowRtcTest + 5000;
+        final PendingIntent alarmPi = getNewMockPendingIntent();
+        setTestAlarm(RTC_WAKEUP, triggerRtc, alarmPi);
+        final long triggerElapsed = triggerRtc - (mNowRtcTest - mNowElapsedTest);
+        assertEquals(triggerElapsed, mTestTimer.getElapsed());
+    }
+
+    @Test
+    public void timeChangeMovesRtcAlarm() throws Exception {
+        mNowElapsedTest = 42;
+        mNowRtcTest = 4123;     // arbitrary values of time
+        final long triggerRtc = mNowRtcTest + 5000;
+        final PendingIntent alarmPi = getNewMockPendingIntent();
+        setTestAlarm(RTC_WAKEUP, triggerRtc, alarmPi);
+        final long triggerElapsed1 = mTestTimer.getElapsed();
+        final long timeDelta = -123;
+        mNowRtcTest += timeDelta;
+        mTestTimer.expire(TIME_CHANGED_MASK);
+        final long triggerElapsed2 = mTestTimer.getElapsed();
+        assertEquals("Invalid movement of triggerElapsed following time change", triggerElapsed2,
+                triggerElapsed1 - timeDelta);
+    }
+
+    @Test
     public void testSingleAlarmExpiration() throws Exception {
         final long triggerTime = mNowElapsedTest + 5000;
         final PendingIntent alarmPi = getNewMockPendingIntent();