Preserve window sizes when rebatching alarms

The existing code wasn't retaining the requested window bounds, if any,
and so could wind up rebatching alarms into much longer potential
delivery windows than originally demanded by the caller.  This could
wind up delivering alarms outside their designated windows entirely.

Bug 11324357

Change-Id: I4d418cd08702e397b3c7692b412d4bf51d5d9e4b
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 3d804ef..b70a34e 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -385,10 +385,19 @@
             for (int i = 0; i < N; i++) {
                 Alarm a = batch.get(i);
                 long whenElapsed = convertToElapsed(a.when, a.type);
-                long maxElapsed = (a.whenElapsed == a.maxWhen)
-                        ? whenElapsed
-                                : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
-                setImplLocked(a.type, a.when, whenElapsed, maxElapsed,
+                final long maxElapsed;
+                if (a.whenElapsed == a.maxWhen) {
+                    // Exact
+                    maxElapsed = whenElapsed;
+                } else {
+                    // Not exact.  Preserve any explicit window, otherwise recalculate
+                    // the window based on the alarm's new futurity.  Note that this
+                    // reflects a policy of preferring timely to deferred delivery.
+                    maxElapsed = (a.windowLength > 0)
+                            ? (whenElapsed + a.windowLength)
+                            : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
+                }
+                setImplLocked(a.type, a.when, whenElapsed, a.windowLength, maxElapsed,
                         a.repeatInterval, a.operation, batch.standalone, doValidate, a.workSource);
             }
         }
@@ -556,15 +565,16 @@
                         + " tElapsed=" + triggerElapsed + " maxElapsed=" + maxElapsed
                         + " interval=" + interval + " standalone=" + isStandalone);
             }
-            setImplLocked(type, triggerAtTime, triggerElapsed, maxElapsed,
+            setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
                     interval, operation, isStandalone, true, workSource);
         }
     }
 
-    private void setImplLocked(int type, long when, long whenElapsed, long maxWhen, long interval,
-            PendingIntent operation, boolean isStandalone, boolean doValidate,
-            WorkSource workSource) {
-        Alarm a = new Alarm(type, when, whenElapsed, maxWhen, interval, operation, workSource);
+    private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
+            long maxWhen, long interval, PendingIntent operation, boolean isStandalone,
+            boolean doValidate, WorkSource workSource) {
+        Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
+                operation, workSource);
         removeLocked(operation);
 
         boolean reschedule;
@@ -1046,7 +1056,7 @@
                     // Also schedule its next recurrence
                     final long delta = alarm.count * alarm.repeatInterval;
                     final long nextElapsed = alarm.whenElapsed + delta;
-                    setImplLocked(alarm.type, alarm.when + delta, nextElapsed,
+                    setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
                             maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
                             alarm.repeatInterval, alarm.operation, batch.standalone, true,
                             alarm.workSource);
@@ -1077,17 +1087,19 @@
         public int type;
         public int count;
         public long when;
+        public long windowLength;
         public long whenElapsed;    // 'when' in the elapsed time base
         public long maxWhen;        // also in the elapsed time base
         public long repeatInterval;
         public PendingIntent operation;
         public WorkSource workSource;
         
-        public Alarm(int _type, long _when, long _whenElapsed, long _maxWhen,
+        public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
                 long _interval, PendingIntent _op, WorkSource _ws) {
             type = _type;
             when = _when;
             whenElapsed = _whenElapsed;
+            windowLength = _windowLength;
             maxWhen = _maxWhen;
             repeatInterval = _interval;
             operation = _op;
@@ -1112,6 +1124,7 @@
             pw.print(prefix); pw.print("type="); pw.print(type);
                     pw.print(" whenElapsed="); pw.print(whenElapsed);
                     pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
+                    pw.print(" window="); pw.print(windowLength);
                     pw.print(" repeatInterval="); pw.print(repeatInterval);
                     pw.print(" count="); pw.println(count);
             pw.print(prefix); pw.print("operation="); pw.println(operation);