diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java index 67d358910de19..232bc08e5b5cb 100644 --- a/services/core/java/com/android/server/attention/AttentionManagerService.java +++ b/services/core/java/com/android/server/attention/AttentionManagerService.java @@ -21,6 +21,7 @@ import static android.service.attention.AttentionService.ATTENTION_FAILURE_CANCE import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNOWN; import android.Manifest; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.attention.AttentionManagerInternal; @@ -85,6 +86,10 @@ public class AttentionManagerService extends SystemService { /** If the check attention called within that period - cached value will be returned. */ private static final long STALE_AFTER_MILLIS = 5_000; + /** The size of the buffer that stores recent attention check results. */ + @VisibleForTesting + protected static final int ATTENTION_CACHE_BUFFER_SIZE = 5; + /** DeviceConfig flag name, if {@code true}, enables AttentionManagerService features. */ private static final String SERVICE_ENABLED = "service_enabled"; private static String sTestAttentionServicePackage; @@ -192,7 +197,8 @@ public class AttentionManagerService extends SystemService { userState.bindLocked(); // throttle frequent requests - final AttentionCheckCache cache = userState.mAttentionCheckCache; + final AttentionCheckCache cache = userState.mAttentionCheckCacheBuffer == null ? null + : userState.mAttentionCheckCacheBuffer.getLast(); if (cache != null && now < cache.mLastComputed + STALE_AFTER_MILLIS) { callbackInternal.onSuccess(cache.mResult, cache.mTimestamp); return true; @@ -236,9 +242,11 @@ public class AttentionManagerService extends SystemService { } synchronized (mLock) { - userState.mAttentionCheckCache = new AttentionCheckCache( - SystemClock.uptimeMillis(), result, - timestamp); + if (userState.mAttentionCheckCacheBuffer == null) { + userState.mAttentionCheckCacheBuffer = new AttentionCheckCacheBuffer(); + } + userState.mAttentionCheckCacheBuffer.add( + new AttentionCheckCache(SystemClock.uptimeMillis(), result, timestamp)); } StatsLog.write( StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED, @@ -421,7 +429,41 @@ public class AttentionManagerService extends SystemService { } } - private static final class AttentionCheckCache { + @VisibleForTesting + protected static final class AttentionCheckCacheBuffer { + private final AttentionCheckCache[] mQueue; + private int mStartIndex; + private int mSize; + + AttentionCheckCacheBuffer() { + mQueue = new AttentionCheckCache[ATTENTION_CACHE_BUFFER_SIZE]; + mStartIndex = 0; + mSize = 0; + } + + public AttentionCheckCache getLast() { + int lastIdx = (mStartIndex + mSize - 1) % ATTENTION_CACHE_BUFFER_SIZE; + return mSize == 0 ? null : mQueue[lastIdx]; + } + + public void add(@NonNull AttentionCheckCache cache) { + int nextIndex = (mStartIndex + mSize) % ATTENTION_CACHE_BUFFER_SIZE; + mQueue[nextIndex] = cache; + if (mSize == ATTENTION_CACHE_BUFFER_SIZE) { + mStartIndex++; + } else { + mSize++; + } + } + + public AttentionCheckCache get(int offset) { + return offset >= mSize ? null + : mQueue[(mStartIndex + offset) % ATTENTION_CACHE_BUFFER_SIZE]; + } + } + + @VisibleForTesting + protected static final class AttentionCheckCache { private final long mLastComputed; private final int mResult; private final long mTimestamp; @@ -463,7 +505,7 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") AttentionCheck mCurrentAttentionCheck; @GuardedBy("mLock") - AttentionCheckCache mAttentionCheckCache; + AttentionCheckCacheBuffer mAttentionCheckCacheBuffer; @GuardedBy("mLock") private boolean mBinding; @@ -532,13 +574,14 @@ public class AttentionManagerService extends SystemService { pw.println("is fulfilled:=" + mCurrentAttentionCheck.mIsFulfilled); pw.decreaseIndent(); } - pw.println("attention check cache:"); - if (mAttentionCheckCache != null) { - pw.increaseIndent(); - pw.println("last computed=" + mAttentionCheckCache.mLastComputed); - pw.println("timestamp=" + mAttentionCheckCache.mTimestamp); - pw.println("result=" + mAttentionCheckCache.mResult); - pw.decreaseIndent(); + if (mAttentionCheckCacheBuffer != null) { + pw.println("attention check cache:"); + for (int i = 0; i < mAttentionCheckCacheBuffer.mSize; i++) { + pw.increaseIndent(); + pw.println("timestamp=" + mAttentionCheckCacheBuffer.get(i).mTimestamp); + pw.println("result=" + mAttentionCheckCacheBuffer.get(i).mResult); + pw.decreaseIndent(); + } } } } diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java index 184dc3dfed62d..a47a5671ccb3c 100644 --- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java @@ -16,6 +16,8 @@ package com.android.server.attention; +import static com.android.server.attention.AttentionManagerService.ATTENTION_CACHE_BUFFER_SIZE; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -39,6 +41,8 @@ import android.service.attention.IAttentionService; import androidx.test.filters.SmallTest; import com.android.server.attention.AttentionManagerService.AttentionCheck; +import com.android.server.attention.AttentionManagerService.AttentionCheckCache; +import com.android.server.attention.AttentionManagerService.AttentionCheckCacheBuffer; import com.android.server.attention.AttentionManagerService.AttentionHandler; import com.android.server.attention.AttentionManagerService.UserState; @@ -56,11 +60,16 @@ public class AttentionManagerServiceTest { private AttentionManagerService mSpyAttentionManager; private UserState mSpyUserState; private final int mTimeout = 1000; - @Mock private AttentionCallbackInternal mMockAttentionCallbackInternal; - @Mock private AttentionHandler mMockHandler; - @Mock private IAttentionCallback mMockIAttentionCallback; - @Mock private IPowerManager mMockIPowerManager; - @Mock Context mContext; + @Mock + private AttentionCallbackInternal mMockAttentionCallbackInternal; + @Mock + private AttentionHandler mMockHandler; + @Mock + private IAttentionCallback mMockIAttentionCallback; + @Mock + private IPowerManager mMockIPowerManager; + @Mock + Context mContext; @Before public void setUp() throws RemoteException { @@ -140,12 +149,45 @@ public class AttentionManagerServiceTest { mSpyAttentionManager.onSwitchUser(userId); } + @Test + public void testAttentionCheckCacheBuffer_getLast_returnTheLastElement() { + AttentionCheckCacheBuffer buffer = new AttentionCheckCacheBuffer(); + buffer.add(new AttentionCheckCache(0, 0, 1L)); + AttentionCheckCache cache = new AttentionCheckCache(0, 0, 2L); + buffer.add(cache); + assertThat(buffer.getLast()).isEqualTo(cache); + } + + @Test + public void testAttentionCheckCacheBuffer_get_returnNullWhenOutOfBoundary() { + AttentionCheckCacheBuffer buffer = new AttentionCheckCacheBuffer(); + assertThat(buffer.get(1)).isNull(); + } + + @Test + public void testAttentionCheckCacheBuffer_get_handleCircularIndexing() { + AttentionCheckCacheBuffer buffer = new AttentionCheckCacheBuffer(); + AttentionCheckCache cache = new AttentionCheckCache(0L, 0, 1L); + // Insert SIZE+1 elements. + for (int i = 0; i <= ATTENTION_CACHE_BUFFER_SIZE; i++) { + if (i == 1) { + buffer.add(cache); + } else { + buffer.add(new AttentionCheckCache(0L, 0, i)); + } + } + // The element that was at index 1 should be at index 0 after inserting SIZE + 1 elements. + assertThat(buffer.get(0)).isEqualTo(cache); + } + private class MockIAttentionService implements IAttentionService { public void checkAttention(IAttentionCallback callback) throws RemoteException { callback.onSuccess(0, 0); } + public void cancelAttentionCheck(IAttentionCallback callback) { } + public IBinder asBinder() { return null; }