Avoid infinite pending MSG_CHECK_IDLE_STATES messages.
1. Make sure we don't have periodic MSG_CHECK_IDLE_STATES messages for
UserHandle.USER_ALL due to Doze allowlist changes.
2. Make sure we don't have duplicate and periodic MSG_CHECK_IDLE_STATES
messages for a single user ID.
Bug: 221352666
Test: atest AppStandbyControllerTests
Change-Id: Iedfe02107f97a4c0b07ec0fc6463500faf286a06
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index cae6cdc..54c3db4 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -104,6 +104,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.view.Display;
import android.widget.Toast;
@@ -260,6 +261,13 @@
private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
+ /**
+ * Set of user IDs and the next time (in the elapsed realtime timebase) when we should check the
+ * apps' idle states.
+ */
+ @GuardedBy("mPendingIdleStateChecks")
+ private final SparseLongArray mPendingIdleStateChecks = new SparseLongArray();
+
// Cache the active network scorer queried from the network scorer service
private volatile String mCachedNetworkScorer = null;
// The last time the network scorer service was queried
@@ -722,7 +730,14 @@
@Override
public void postCheckIdleStates(int userId) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
+ if (userId == UserHandle.USER_ALL) {
+ postOneTimeCheckIdleStates();
+ } else {
+ synchronized (mPendingIdleStateChecks) {
+ mPendingIdleStateChecks.put(userId, mInjector.elapsedRealtime());
+ }
+ mHandler.obtainMessage(MSG_CHECK_IDLE_STATES).sendToTarget();
+ }
}
@Override
@@ -2374,10 +2389,32 @@
break;
case MSG_CHECK_IDLE_STATES:
- if (checkIdleStates(msg.arg1) && mAppIdleEnabled) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_CHECK_IDLE_STATES, msg.arg1, 0),
- mCheckIdleIntervalMillis);
+ removeMessages(MSG_CHECK_IDLE_STATES);
+
+ long earliestCheck = Long.MAX_VALUE;
+ final long nowElapsed = mInjector.elapsedRealtime();
+ synchronized (mPendingIdleStateChecks) {
+ for (int i = mPendingIdleStateChecks.size() - 1; i >= 0; --i) {
+ long expirationTime = mPendingIdleStateChecks.valueAt(i);
+
+ if (expirationTime <= nowElapsed) {
+ final int userId = mPendingIdleStateChecks.keyAt(i);
+ if (checkIdleStates(userId) && mAppIdleEnabled) {
+ expirationTime = nowElapsed + mCheckIdleIntervalMillis;
+ mPendingIdleStateChecks.put(userId, expirationTime);
+ } else {
+ mPendingIdleStateChecks.removeAt(i);
+ continue;
+ }
+ }
+
+ earliestCheck = Math.min(earliestCheck, expirationTime);
+ }
+ }
+ if (earliestCheck != Long.MAX_VALUE) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_CHECK_IDLE_STATES),
+ earliestCheck - nowElapsed);
}
break;