Deal with alarm times near MAX_VALUE
Avoid overflow across the end of time when calculating window endpoints, and
clamp recurrence periods at something workable but too long to be plausible.
Bug: 70536740
Bug: 72658919
Test: manual
Test: atest CtsAppTestCases:AlarmManagerTest
Change-Id: Icb7a571802b722499df09833522d1d3f28607621
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
index d1c5db6..b288c11 100644
--- a/core/proto/android/server/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -220,6 +220,8 @@
optional int64 allow_while_idle_long_duration_ms = 5;
// BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
optional int64 allow_while_idle_whitelist_duration_ms = 6;
+ // Maximum alarm recurrence interval.
+ optional int64 max_interval_duration_ms = 7;
}
// A com.android.server.AlarmManagerService.FilterStats object.
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index c93f405..62a7b8f 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -66,6 +66,7 @@
import android.system.Os;
import android.text.TextUtils;
import android.text.format.DateFormat;
+import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.KeyValueListParser;
@@ -268,6 +269,7 @@
// Key names stored in the settings value.
private static final String KEY_MIN_FUTURITY = "min_futurity";
private static final String KEY_MIN_INTERVAL = "min_interval";
+ private static final String KEY_MAX_INTERVAL = "max_interval";
private static final String KEY_ALLOW_WHILE_IDLE_SHORT_TIME = "allow_while_idle_short_time";
private static final String KEY_ALLOW_WHILE_IDLE_LONG_TIME = "allow_while_idle_long_time";
private static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
@@ -285,6 +287,7 @@
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
+ private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY;
private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
@@ -303,6 +306,9 @@
// Minimum alarm recurrence interval
public long MIN_INTERVAL = DEFAULT_MIN_INTERVAL;
+ // Maximum alarm recurrence interval
+ public long MAX_INTERVAL = DEFAULT_MAX_INTERVAL;
+
// Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle.
public long ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME;
@@ -361,6 +367,7 @@
MIN_FUTURITY = mParser.getLong(KEY_MIN_FUTURITY, DEFAULT_MIN_FUTURITY);
MIN_INTERVAL = mParser.getLong(KEY_MIN_INTERVAL, DEFAULT_MIN_INTERVAL);
+ MAX_INTERVAL = mParser.getLong(KEY_MAX_INTERVAL, DEFAULT_MAX_INTERVAL);
ALLOW_WHILE_IDLE_SHORT_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME,
DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME);
ALLOW_WHILE_IDLE_LONG_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME,
@@ -391,6 +398,10 @@
TimeUtils.formatDuration(MIN_INTERVAL, pw);
pw.println();
+ pw.print(" "); pw.print(KEY_MAX_INTERVAL); pw.print("=");
+ TimeUtils.formatDuration(MAX_INTERVAL, pw);
+ pw.println();
+
pw.print(" "); pw.print(KEY_LISTENER_TIMEOUT); pw.print("=");
TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
pw.println();
@@ -419,6 +430,7 @@
proto.write(ConstantsProto.MIN_FUTURITY_DURATION_MS, MIN_FUTURITY);
proto.write(ConstantsProto.MIN_INTERVAL_DURATION_MS, MIN_INTERVAL);
+ proto.write(ConstantsProto.MAX_INTERVAL_DURATION_MS, MAX_INTERVAL);
proto.write(ConstantsProto.LISTENER_TIMEOUT_DURATION_MS, LISTENER_TIMEOUT);
proto.write(ConstantsProto.ALLOW_WHILE_IDLE_SHORT_DURATION_MS,
ALLOW_WHILE_IDLE_SHORT_TIME);
@@ -481,7 +493,7 @@
Batch(Alarm seed) {
start = seed.whenElapsed;
- end = seed.maxWhenElapsed;
+ end = clampPositive(seed.maxWhenElapsed);
flags = seed.flags;
alarms.add(seed);
if (seed.operation == mTimeTickSender) {
@@ -737,7 +749,7 @@
if (futurity < MIN_FUZZABLE_INTERVAL) {
futurity = 0;
}
- return triggerAtTime + (long)(.75 * futurity);
+ return clampPositive(triggerAtTime + (long)(.75 * futurity));
}
// returns true if the batch was added at the head
@@ -913,7 +925,7 @@
// 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)
+ ? clampPositive(whenElapsed + a.windowLength)
: maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
}
a.whenElapsed = whenElapsed;
@@ -921,6 +933,10 @@
setImplLocked(a, true, doValidate);
}
+ static long clampPositive(long val) {
+ return (val >= 0) ? val : Long.MAX_VALUE;
+ }
+
/**
* Sends alarms that were blocked due to user applied background restrictions - either because
* the user lifted those or the uid came to foreground.
@@ -1421,13 +1437,18 @@
}
// Sanity check the recurrence interval. This will catch people who supply
- // seconds when the API expects milliseconds.
+ // seconds when the API expects milliseconds, or apps trying shenanigans
+ // around intentional period overflow, etc.
final long minInterval = mConstants.MIN_INTERVAL;
if (interval > 0 && interval < minInterval) {
Slog.w(TAG, "Suspiciously short interval " + interval
+ " millis; expanding to " + (minInterval/1000)
+ " seconds");
interval = minInterval;
+ } else if (interval > mConstants.MAX_INTERVAL) {
+ Slog.w(TAG, "Suspiciously long interval " + interval
+ + " millis; clamping");
+ interval = mConstants.MAX_INTERVAL;
}
if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) {
@@ -3175,8 +3196,7 @@
whenElapsed = _whenElapsed;
expectedWhenElapsed = _whenElapsed;
windowLength = _windowLength;
- maxWhenElapsed = _maxWhen;
- expectedMaxWhenElapsed = _maxWhen;
+ maxWhenElapsed = expectedMaxWhenElapsed = clampPositive(_maxWhen);
repeatInterval = _interval;
operation = _op;
listener = _rec;