Loading services/core/java/com/android/server/attention/AttentionManagerService.java +133 −219 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNO import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.attention.AttentionManagerInternal; import android.attention.AttentionManagerInternal.AttentionCallbackInternal; import android.content.BroadcastReceiver; Loading Loading @@ -55,7 +54,6 @@ import android.service.attention.IAttentionCallback; import android.service.attention.IAttentionService; import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -101,17 +99,26 @@ public class AttentionManagerService extends SystemService { @VisibleForTesting protected static final int ATTENTION_CACHE_BUFFER_SIZE = 5; private final AttentionServiceConnection mConnection = new AttentionServiceConnection(); private static String sTestAttentionServicePackage; private final Context mContext; private final PowerManager mPowerManager; private final Object mLock; @GuardedBy("mLock") private final SparseArray<UserState> mUserStates = new SparseArray<>(); private IAttentionService mService; @GuardedBy("mLock") private AttentionCheckCacheBuffer mAttentionCheckCacheBuffer; @GuardedBy("mLock") private boolean mBinding; private AttentionHandler mAttentionHandler; @VisibleForTesting ComponentName mComponentName; @VisibleForTesting @GuardedBy("mLock") AttentionCheck mCurrentAttentionCheck; public AttentionManagerService(Context context) { this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE), new Object(), null); Loading Loading @@ -142,11 +149,6 @@ public class AttentionManagerService extends SystemService { publishLocalService(AttentionManagerInternal.class, new LocalService()); } @Override public void onSwitchUser(int userId) { cancelAndUnbindLocked(peekUserStateLocked(userId)); } /** Returns {@code true} if attention service is configured on this device. */ public static boolean isServiceConfigured(Context context) { return !TextUtils.isEmpty(getServiceConfigPackage(context)); Loading Loading @@ -219,35 +221,33 @@ public class AttentionManagerService extends SystemService { // schedule shutting down the connection if no one resets this timer freeIfInactiveLocked(); final UserState userState = getOrCreateCurrentUserStateLocked(); // lazily start the service, which should be very lightweight to start userState.bindLocked(); bindLocked(); // throttle frequent requests final AttentionCheckCache cache = userState.mAttentionCheckCacheBuffer == null ? null : userState.mAttentionCheckCacheBuffer.getLast(); final AttentionCheckCache cache = mAttentionCheckCacheBuffer == null ? null : mAttentionCheckCacheBuffer.getLast(); if (cache != null && now < cache.mLastComputed + getStaleAfterMillis()) { callbackInternal.onSuccess(cache.mResult, cache.mTimestamp); return true; } // prevent spamming with multiple requests, only one at a time is allowed if (userState.mCurrentAttentionCheck != null) { if (!userState.mCurrentAttentionCheck.mIsDispatched || !userState.mCurrentAttentionCheck.mIsFulfilled) { if (mCurrentAttentionCheck != null) { if (!mCurrentAttentionCheck.mIsDispatched || !mCurrentAttentionCheck.mIsFulfilled) { return false; } } userState.mCurrentAttentionCheck = new AttentionCheck(callbackInternal, userState); mCurrentAttentionCheck = new AttentionCheck(callbackInternal, this); if (userState.mService != null) { if (mService != null) { try { // schedule request cancellation if not returned by that point yet cancelAfterTimeoutLocked(timeout); userState.mService.checkAttention( userState.mCurrentAttentionCheck.mIAttentionCallback); userState.mCurrentAttentionCheck.mIsDispatched = true; mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback); mCurrentAttentionCheck.mIsDispatched = true; } catch (RemoteException e) { Slog.e(LOG_TAG, "Cannot call into the AttentionService"); return false; Loading @@ -261,15 +261,11 @@ public class AttentionManagerService extends SystemService { @VisibleForTesting void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { synchronized (mLock) { final UserState userState = peekCurrentUserStateLocked(); if (userState == null) { return; } if (!userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) { if (!mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) { Slog.w(LOG_TAG, "Cannot cancel a non-current request"); return; } cancel(userState); cancel(); } } Loading @@ -290,39 +286,6 @@ public class AttentionManagerService extends SystemService { timeout); } @GuardedBy("mLock") @VisibleForTesting protected UserState getOrCreateCurrentUserStateLocked() { // Doesn't need to cache the states of different users. return getOrCreateUserStateLocked(0); } @GuardedBy("mLock") @VisibleForTesting protected UserState getOrCreateUserStateLocked(int userId) { UserState result = mUserStates.get(userId); if (result == null) { result = new UserState(userId, mContext, mLock, mAttentionHandler, mComponentName); mUserStates.put(userId, result); } return result; } @GuardedBy("mLock") @Nullable @VisibleForTesting protected UserState peekCurrentUserStateLocked() { // Doesn't need to cache the states of different users. return peekUserStateLocked(0); } @GuardedBy("mLock") @Nullable private UserState peekUserStateLocked(int userId) { return mUserStates.get(userId); } private static String getServiceConfigPackage(Context context) { return context.getPackageManager().getAttentionServicePackageName(); } Loading Loading @@ -380,21 +343,14 @@ public class AttentionManagerService extends SystemService { ipw.println("Class=" + mComponentName.getClassName()); ipw.decreaseIndent(); } ipw.println("binding=" + mBinding); ipw.println("current attention check:"); synchronized (mLock) { int size = mUserStates.size(); ipw.print("Number user states: "); ipw.println(size); if (size > 0) { ipw.increaseIndent(); for (int i = 0; i < size; i++) { UserState userState = mUserStates.valueAt(i); ipw.print(i); ipw.print(":"); userState.dump(ipw); ipw.println(); if (mCurrentAttentionCheck != null) { mCurrentAttentionCheck.dump(ipw); } ipw.decreaseIndent(); if (mAttentionCheckCacheBuffer != null) { mAttentionCheckCacheBuffer.dump(ipw); } } } Loading Loading @@ -447,6 +403,20 @@ public class AttentionManagerService extends SystemService { return offset >= mSize ? null : mQueue[(mStartIndex + offset) % ATTENTION_CACHE_BUFFER_SIZE]; } private void dump(IndentingPrintWriter ipw) { ipw.println("attention check cache:"); AttentionCheckCache cache; for (int i = 0; i < mSize; i++) { cache = get(i); if (cache != null) { ipw.increaseIndent(); ipw.println("timestamp=" + cache.mTimestamp); ipw.println("result=" + cache.mResult); ipw.decreaseIndent(); } } } } @VisibleForTesting Loading @@ -471,7 +441,8 @@ public class AttentionManagerService extends SystemService { private boolean mIsDispatched; private boolean mIsFulfilled; AttentionCheck(AttentionCallbackInternal callbackInternal, UserState userState) { AttentionCheck(AttentionCallbackInternal callbackInternal, AttentionManagerService service) { mCallbackInternal = callbackInternal; mIAttentionCallback = new IAttentionCallback.Stub() { @Override Loading @@ -482,7 +453,7 @@ public class AttentionManagerService extends SystemService { mIsFulfilled = true; callbackInternal.onSuccess(result, timestamp); logStats(result); userState.appendResultToAttentionCacheBuffer( service.appendResultToAttentionCacheBuffer( new AttentionCheckCache(SystemClock.uptimeMillis(), result, timestamp)); } Loading @@ -509,96 +480,12 @@ public class AttentionManagerService extends SystemService { mIsFulfilled = true; mCallbackInternal.onFailure(ATTENTION_FAILURE_CANCELLED); } } @VisibleForTesting protected static class UserState { private final ComponentName mComponentName; private final AttentionServiceConnection mConnection = new AttentionServiceConnection(); @GuardedBy("mLock") IAttentionService mService; @GuardedBy("mLock") AttentionCheck mCurrentAttentionCheck; @GuardedBy("mLock") AttentionCheckCacheBuffer mAttentionCheckCacheBuffer; @GuardedBy("mLock") private boolean mBinding; @UserIdInt private final int mUserId; private final Context mContext; private final Object mLock; private final Handler mAttentionHandler; UserState(int userId, Context context, Object lock, Handler handler, ComponentName componentName) { mUserId = userId; mContext = Objects.requireNonNull(context); mLock = Objects.requireNonNull(lock); mComponentName = Objects.requireNonNull(componentName); mAttentionHandler = handler; } @GuardedBy("mLock") private void handlePendingCallbackLocked() { if (!mCurrentAttentionCheck.mIsDispatched) { if (mService != null) { try { mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback); mCurrentAttentionCheck.mIsDispatched = true; } catch (RemoteException e) { Slog.e(LOG_TAG, "Cannot call into the AttentionService"); } } else { mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN); } } } /** Binds to the system's AttentionService which provides an actual implementation. */ @GuardedBy("mLock") private void bindLocked() { // No need to bind if service is binding or has already been bound. if (mBinding || mService != null) { return; } mBinding = true; // mContext.bindServiceAsUser() calls into ActivityManagerService which it may already // hold the lock and had called into PowerManagerService, which holds a lock. // That would create a deadlock. To solve that, putting it on a handler. mAttentionHandler.post(() -> { final Intent serviceIntent = new Intent( AttentionService.SERVICE_INTERFACE).setComponent( mComponentName); // Note: no reason to clear the calling identity, we won't have one in a handler. mContext.bindServiceAsUser(serviceIntent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT); }); } private void dump(IndentingPrintWriter pw) { pw.println("userId=" + mUserId); synchronized (mLock) { pw.println("binding=" + mBinding); pw.println("current attention check:"); if (mCurrentAttentionCheck != null) { pw.increaseIndent(); pw.println("is dispatched=" + mCurrentAttentionCheck.mIsDispatched); pw.println("is fulfilled:=" + mCurrentAttentionCheck.mIsFulfilled); 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(); } } void dump(IndentingPrintWriter ipw) { ipw.increaseIndent(); ipw.println("is dispatched=" + mIsDispatched); ipw.println("is fulfilled:=" + mIsFulfilled); ipw.decreaseIndent(); } } Loading Loading @@ -645,6 +532,21 @@ public class AttentionManagerService extends SystemService { } } } @GuardedBy("mLock") private void handlePendingCallbackLocked() { if (mCurrentAttentionCheck != null && !mCurrentAttentionCheck.mIsDispatched) { if (mService != null) { try { mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback); mCurrentAttentionCheck.mIsDispatched = true; } catch (RemoteException e) { Slog.e(LOG_TAG, "Cannot call into the AttentionService"); } } else { mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN); } } } @VisibleForTesting Loading @@ -661,16 +563,14 @@ public class AttentionManagerService extends SystemService { switch (msg.what) { // Do not occupy resources when not in use - unbind proactively. case CHECK_CONNECTION_EXPIRATION: { for (int i = 0; i < mUserStates.size(); i++) { cancelAndUnbindLocked(mUserStates.valueAt(i)); } cancelAndUnbindLocked(); } break; // Callee is no longer interested in the attention check result - cancel. case ATTENTION_CHECK_TIMEOUT: { synchronized (mLock) { cancel(peekCurrentUserStateLocked()); cancel(); } } break; Loading @@ -682,54 +582,69 @@ public class AttentionManagerService extends SystemService { } @VisibleForTesting void cancel(UserState userState) { if (userState == null || userState.mCurrentAttentionCheck == null) { return; } if (userState.mCurrentAttentionCheck.mIsFulfilled) { @GuardedBy("mLock") void cancel() { if (mCurrentAttentionCheck.mIsFulfilled) { if (DEBUG) { Slog.d(LOG_TAG, "Trying to cancel the check that has been already fulfilled."); } return; } if (userState.mService == null) { userState.mCurrentAttentionCheck.cancelInternal(); if (mService == null) { mCurrentAttentionCheck.cancelInternal(); return; } try { userState.mService.cancelAttentionCheck( userState.mCurrentAttentionCheck.mIAttentionCallback); mService.cancelAttentionCheck(mCurrentAttentionCheck.mIAttentionCallback); } catch (RemoteException e) { Slog.e(LOG_TAG, "Unable to cancel attention check"); userState.mCurrentAttentionCheck.cancelInternal(); mCurrentAttentionCheck.cancelInternal(); } } @GuardedBy("mLock") private void cancelAndUnbindLocked(UserState userState) { private void cancelAndUnbindLocked() { synchronized (mLock) { if (userState == null) { if (mCurrentAttentionCheck == null) { return; } cancel(userState); if (userState.mService == null) { cancel(); if (mService == null) { return; } mAttentionHandler.post(() -> mContext.unbindService(userState.mConnection)); mAttentionHandler.post(() -> mContext.unbindService(mConnection)); // Note: this will set mBinding to false even though it could still be trying to bind // (i.e. the runnable was posted in bindLocked but then cancelAndUnbindLocked was // called before it's run yet). This is a safe state at the moment, // since it will eventually, but feels like a source for confusion down the road and // may cause some expensive and unnecessary work to be done. userState.mConnection.cleanupService(); mUserStates.remove(userState.mUserId); mConnection.cleanupService(); } } /** Binds to the system's AttentionService which provides an actual implementation. */ @GuardedBy("mLock") private void bindLocked() { // No need to bind if service is binding or has already been bound. if (mBinding || mService != null) { return; } mBinding = true; // mContext.bindServiceAsUser() calls into ActivityManagerService which it may already // hold the lock and had called into PowerManagerService, which holds a lock. // That would create a deadlock. To solve that, putting it on a handler. mAttentionHandler.post(() -> { final Intent serviceIntent = new Intent( AttentionService.SERVICE_INTERFACE).setComponent( mComponentName); // Note: no reason to clear the calling identity, we won't have one in a handler. mContext.bindServiceAsUser(serviceIntent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT); }); } /** Loading @@ -740,7 +655,7 @@ public class AttentionManagerService extends SystemService { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { cancelAndUnbindLocked(peekCurrentUserStateLocked()); cancelAndUnbindLocked(); } } } Loading Loading @@ -853,7 +768,6 @@ public class AttentionManagerService extends SystemService { private void resetStates() { mComponentName = resolveAttentionService(mContext); mUserStates.clear(); } @Override Loading services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java +7 −33 Original line number Diff line number Diff line Loading @@ -29,7 +29,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.attention.AttentionManagerInternal.AttentionCallbackInternal; Loading @@ -50,7 +49,6 @@ 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; import org.junit.Before; import org.junit.Test; Loading @@ -64,15 +62,12 @@ import org.mockito.MockitoAnnotations; @SmallTest public class AttentionManagerServiceTest { private AttentionManagerService mSpyAttentionManager; private UserState mSpyUserState; private final int mTimeout = 1000; @Mock private AttentionCallbackInternal mMockAttentionCallbackInternal; @Mock private AttentionHandler mMockHandler; @Mock private UserState mMockUserState; @Mock private IPowerManager mMockIPowerManager; @Mock private IThermalService mMockIThermalService; Loading @@ -91,44 +86,31 @@ public class AttentionManagerServiceTest { Object mLock = new Object(); // setup a spy on attention manager AttentionManagerService mAttentionManager = new AttentionManagerService( AttentionManagerService attentionManager = new AttentionManagerService( mContext, mPowerManager, mLock, mMockHandler); mSpyAttentionManager = Mockito.spy(mAttentionManager); mSpyAttentionManager = Mockito.spy(attentionManager); // setup a spy on user state ComponentName componentName = new ComponentName("a", "b"); mSpyAttentionManager.mComponentName = componentName; UserState mUserState = new UserState(0, mContext, mLock, mMockHandler, componentName); mUserState.mService = new MockIAttentionService(); mSpyUserState = spy(mUserState); } @Test public void testCancelAttentionCheck_noCrashWhenNoUserStateLocked() { mSpyAttentionManager.cancelAttentionCheck(null); AttentionCheck attentionCheck = new AttentionCheck(mMockAttentionCallbackInternal, mSpyAttentionManager); mSpyAttentionManager.mCurrentAttentionCheck = attentionCheck; } @Test public void testCancelAttentionCheck_noCrashWhenCallbackMismatched() { mSpyUserState.mCurrentAttentionCheck = new AttentionCheck(mMockAttentionCallbackInternal, mMockUserState); doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked(); assertThat(mMockAttentionCallbackInternal).isNotNull(); mSpyAttentionManager.cancelAttentionCheck(null); } @Test public void testCancelAttentionCheck_cancelCallbackWhenMatched() { mSpyUserState.mCurrentAttentionCheck = new AttentionCheck(mMockAttentionCallbackInternal, mMockUserState); doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked(); mSpyAttentionManager.cancelAttentionCheck(mMockAttentionCallbackInternal); verify(mSpyAttentionManager).cancel(any()); verify(mSpyAttentionManager).cancel(); } @Test Loading @@ -142,7 +124,6 @@ public class AttentionManagerServiceTest { public void testCheckAttention_callOnSuccess() throws RemoteException { doReturn(true).when(mSpyAttentionManager).isServiceEnabled(); doReturn(true).when(mMockIPowerManager).isInteractive(); doReturn(mSpyUserState).when(mSpyAttentionManager).getOrCreateCurrentUserStateLocked(); doNothing().when(mSpyAttentionManager).freeIfInactiveLocked(); AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class); Loading @@ -150,13 +131,6 @@ public class AttentionManagerServiceTest { verify(callback).onSuccess(anyInt(), anyLong()); } @Test public void testOnSwitchUser_noCrashCurrentServiceIsNull() { final int userId = 10; mSpyAttentionManager.getOrCreateUserStateLocked(userId); mSpyAttentionManager.onSwitchUser(userId); } @Test public void testAttentionCheckCacheBuffer_getLast_returnTheLastElement() { AttentionCheckCacheBuffer buffer = new AttentionCheckCacheBuffer(); Loading Loading
services/core/java/com/android/server/attention/AttentionManagerService.java +133 −219 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNO import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.attention.AttentionManagerInternal; import android.attention.AttentionManagerInternal.AttentionCallbackInternal; import android.content.BroadcastReceiver; Loading Loading @@ -55,7 +54,6 @@ import android.service.attention.IAttentionCallback; import android.service.attention.IAttentionService; import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -101,17 +99,26 @@ public class AttentionManagerService extends SystemService { @VisibleForTesting protected static final int ATTENTION_CACHE_BUFFER_SIZE = 5; private final AttentionServiceConnection mConnection = new AttentionServiceConnection(); private static String sTestAttentionServicePackage; private final Context mContext; private final PowerManager mPowerManager; private final Object mLock; @GuardedBy("mLock") private final SparseArray<UserState> mUserStates = new SparseArray<>(); private IAttentionService mService; @GuardedBy("mLock") private AttentionCheckCacheBuffer mAttentionCheckCacheBuffer; @GuardedBy("mLock") private boolean mBinding; private AttentionHandler mAttentionHandler; @VisibleForTesting ComponentName mComponentName; @VisibleForTesting @GuardedBy("mLock") AttentionCheck mCurrentAttentionCheck; public AttentionManagerService(Context context) { this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE), new Object(), null); Loading Loading @@ -142,11 +149,6 @@ public class AttentionManagerService extends SystemService { publishLocalService(AttentionManagerInternal.class, new LocalService()); } @Override public void onSwitchUser(int userId) { cancelAndUnbindLocked(peekUserStateLocked(userId)); } /** Returns {@code true} if attention service is configured on this device. */ public static boolean isServiceConfigured(Context context) { return !TextUtils.isEmpty(getServiceConfigPackage(context)); Loading Loading @@ -219,35 +221,33 @@ public class AttentionManagerService extends SystemService { // schedule shutting down the connection if no one resets this timer freeIfInactiveLocked(); final UserState userState = getOrCreateCurrentUserStateLocked(); // lazily start the service, which should be very lightweight to start userState.bindLocked(); bindLocked(); // throttle frequent requests final AttentionCheckCache cache = userState.mAttentionCheckCacheBuffer == null ? null : userState.mAttentionCheckCacheBuffer.getLast(); final AttentionCheckCache cache = mAttentionCheckCacheBuffer == null ? null : mAttentionCheckCacheBuffer.getLast(); if (cache != null && now < cache.mLastComputed + getStaleAfterMillis()) { callbackInternal.onSuccess(cache.mResult, cache.mTimestamp); return true; } // prevent spamming with multiple requests, only one at a time is allowed if (userState.mCurrentAttentionCheck != null) { if (!userState.mCurrentAttentionCheck.mIsDispatched || !userState.mCurrentAttentionCheck.mIsFulfilled) { if (mCurrentAttentionCheck != null) { if (!mCurrentAttentionCheck.mIsDispatched || !mCurrentAttentionCheck.mIsFulfilled) { return false; } } userState.mCurrentAttentionCheck = new AttentionCheck(callbackInternal, userState); mCurrentAttentionCheck = new AttentionCheck(callbackInternal, this); if (userState.mService != null) { if (mService != null) { try { // schedule request cancellation if not returned by that point yet cancelAfterTimeoutLocked(timeout); userState.mService.checkAttention( userState.mCurrentAttentionCheck.mIAttentionCallback); userState.mCurrentAttentionCheck.mIsDispatched = true; mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback); mCurrentAttentionCheck.mIsDispatched = true; } catch (RemoteException e) { Slog.e(LOG_TAG, "Cannot call into the AttentionService"); return false; Loading @@ -261,15 +261,11 @@ public class AttentionManagerService extends SystemService { @VisibleForTesting void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { synchronized (mLock) { final UserState userState = peekCurrentUserStateLocked(); if (userState == null) { return; } if (!userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) { if (!mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) { Slog.w(LOG_TAG, "Cannot cancel a non-current request"); return; } cancel(userState); cancel(); } } Loading @@ -290,39 +286,6 @@ public class AttentionManagerService extends SystemService { timeout); } @GuardedBy("mLock") @VisibleForTesting protected UserState getOrCreateCurrentUserStateLocked() { // Doesn't need to cache the states of different users. return getOrCreateUserStateLocked(0); } @GuardedBy("mLock") @VisibleForTesting protected UserState getOrCreateUserStateLocked(int userId) { UserState result = mUserStates.get(userId); if (result == null) { result = new UserState(userId, mContext, mLock, mAttentionHandler, mComponentName); mUserStates.put(userId, result); } return result; } @GuardedBy("mLock") @Nullable @VisibleForTesting protected UserState peekCurrentUserStateLocked() { // Doesn't need to cache the states of different users. return peekUserStateLocked(0); } @GuardedBy("mLock") @Nullable private UserState peekUserStateLocked(int userId) { return mUserStates.get(userId); } private static String getServiceConfigPackage(Context context) { return context.getPackageManager().getAttentionServicePackageName(); } Loading Loading @@ -380,21 +343,14 @@ public class AttentionManagerService extends SystemService { ipw.println("Class=" + mComponentName.getClassName()); ipw.decreaseIndent(); } ipw.println("binding=" + mBinding); ipw.println("current attention check:"); synchronized (mLock) { int size = mUserStates.size(); ipw.print("Number user states: "); ipw.println(size); if (size > 0) { ipw.increaseIndent(); for (int i = 0; i < size; i++) { UserState userState = mUserStates.valueAt(i); ipw.print(i); ipw.print(":"); userState.dump(ipw); ipw.println(); if (mCurrentAttentionCheck != null) { mCurrentAttentionCheck.dump(ipw); } ipw.decreaseIndent(); if (mAttentionCheckCacheBuffer != null) { mAttentionCheckCacheBuffer.dump(ipw); } } } Loading Loading @@ -447,6 +403,20 @@ public class AttentionManagerService extends SystemService { return offset >= mSize ? null : mQueue[(mStartIndex + offset) % ATTENTION_CACHE_BUFFER_SIZE]; } private void dump(IndentingPrintWriter ipw) { ipw.println("attention check cache:"); AttentionCheckCache cache; for (int i = 0; i < mSize; i++) { cache = get(i); if (cache != null) { ipw.increaseIndent(); ipw.println("timestamp=" + cache.mTimestamp); ipw.println("result=" + cache.mResult); ipw.decreaseIndent(); } } } } @VisibleForTesting Loading @@ -471,7 +441,8 @@ public class AttentionManagerService extends SystemService { private boolean mIsDispatched; private boolean mIsFulfilled; AttentionCheck(AttentionCallbackInternal callbackInternal, UserState userState) { AttentionCheck(AttentionCallbackInternal callbackInternal, AttentionManagerService service) { mCallbackInternal = callbackInternal; mIAttentionCallback = new IAttentionCallback.Stub() { @Override Loading @@ -482,7 +453,7 @@ public class AttentionManagerService extends SystemService { mIsFulfilled = true; callbackInternal.onSuccess(result, timestamp); logStats(result); userState.appendResultToAttentionCacheBuffer( service.appendResultToAttentionCacheBuffer( new AttentionCheckCache(SystemClock.uptimeMillis(), result, timestamp)); } Loading @@ -509,96 +480,12 @@ public class AttentionManagerService extends SystemService { mIsFulfilled = true; mCallbackInternal.onFailure(ATTENTION_FAILURE_CANCELLED); } } @VisibleForTesting protected static class UserState { private final ComponentName mComponentName; private final AttentionServiceConnection mConnection = new AttentionServiceConnection(); @GuardedBy("mLock") IAttentionService mService; @GuardedBy("mLock") AttentionCheck mCurrentAttentionCheck; @GuardedBy("mLock") AttentionCheckCacheBuffer mAttentionCheckCacheBuffer; @GuardedBy("mLock") private boolean mBinding; @UserIdInt private final int mUserId; private final Context mContext; private final Object mLock; private final Handler mAttentionHandler; UserState(int userId, Context context, Object lock, Handler handler, ComponentName componentName) { mUserId = userId; mContext = Objects.requireNonNull(context); mLock = Objects.requireNonNull(lock); mComponentName = Objects.requireNonNull(componentName); mAttentionHandler = handler; } @GuardedBy("mLock") private void handlePendingCallbackLocked() { if (!mCurrentAttentionCheck.mIsDispatched) { if (mService != null) { try { mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback); mCurrentAttentionCheck.mIsDispatched = true; } catch (RemoteException e) { Slog.e(LOG_TAG, "Cannot call into the AttentionService"); } } else { mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN); } } } /** Binds to the system's AttentionService which provides an actual implementation. */ @GuardedBy("mLock") private void bindLocked() { // No need to bind if service is binding or has already been bound. if (mBinding || mService != null) { return; } mBinding = true; // mContext.bindServiceAsUser() calls into ActivityManagerService which it may already // hold the lock and had called into PowerManagerService, which holds a lock. // That would create a deadlock. To solve that, putting it on a handler. mAttentionHandler.post(() -> { final Intent serviceIntent = new Intent( AttentionService.SERVICE_INTERFACE).setComponent( mComponentName); // Note: no reason to clear the calling identity, we won't have one in a handler. mContext.bindServiceAsUser(serviceIntent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT); }); } private void dump(IndentingPrintWriter pw) { pw.println("userId=" + mUserId); synchronized (mLock) { pw.println("binding=" + mBinding); pw.println("current attention check:"); if (mCurrentAttentionCheck != null) { pw.increaseIndent(); pw.println("is dispatched=" + mCurrentAttentionCheck.mIsDispatched); pw.println("is fulfilled:=" + mCurrentAttentionCheck.mIsFulfilled); 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(); } } void dump(IndentingPrintWriter ipw) { ipw.increaseIndent(); ipw.println("is dispatched=" + mIsDispatched); ipw.println("is fulfilled:=" + mIsFulfilled); ipw.decreaseIndent(); } } Loading Loading @@ -645,6 +532,21 @@ public class AttentionManagerService extends SystemService { } } } @GuardedBy("mLock") private void handlePendingCallbackLocked() { if (mCurrentAttentionCheck != null && !mCurrentAttentionCheck.mIsDispatched) { if (mService != null) { try { mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback); mCurrentAttentionCheck.mIsDispatched = true; } catch (RemoteException e) { Slog.e(LOG_TAG, "Cannot call into the AttentionService"); } } else { mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN); } } } @VisibleForTesting Loading @@ -661,16 +563,14 @@ public class AttentionManagerService extends SystemService { switch (msg.what) { // Do not occupy resources when not in use - unbind proactively. case CHECK_CONNECTION_EXPIRATION: { for (int i = 0; i < mUserStates.size(); i++) { cancelAndUnbindLocked(mUserStates.valueAt(i)); } cancelAndUnbindLocked(); } break; // Callee is no longer interested in the attention check result - cancel. case ATTENTION_CHECK_TIMEOUT: { synchronized (mLock) { cancel(peekCurrentUserStateLocked()); cancel(); } } break; Loading @@ -682,54 +582,69 @@ public class AttentionManagerService extends SystemService { } @VisibleForTesting void cancel(UserState userState) { if (userState == null || userState.mCurrentAttentionCheck == null) { return; } if (userState.mCurrentAttentionCheck.mIsFulfilled) { @GuardedBy("mLock") void cancel() { if (mCurrentAttentionCheck.mIsFulfilled) { if (DEBUG) { Slog.d(LOG_TAG, "Trying to cancel the check that has been already fulfilled."); } return; } if (userState.mService == null) { userState.mCurrentAttentionCheck.cancelInternal(); if (mService == null) { mCurrentAttentionCheck.cancelInternal(); return; } try { userState.mService.cancelAttentionCheck( userState.mCurrentAttentionCheck.mIAttentionCallback); mService.cancelAttentionCheck(mCurrentAttentionCheck.mIAttentionCallback); } catch (RemoteException e) { Slog.e(LOG_TAG, "Unable to cancel attention check"); userState.mCurrentAttentionCheck.cancelInternal(); mCurrentAttentionCheck.cancelInternal(); } } @GuardedBy("mLock") private void cancelAndUnbindLocked(UserState userState) { private void cancelAndUnbindLocked() { synchronized (mLock) { if (userState == null) { if (mCurrentAttentionCheck == null) { return; } cancel(userState); if (userState.mService == null) { cancel(); if (mService == null) { return; } mAttentionHandler.post(() -> mContext.unbindService(userState.mConnection)); mAttentionHandler.post(() -> mContext.unbindService(mConnection)); // Note: this will set mBinding to false even though it could still be trying to bind // (i.e. the runnable was posted in bindLocked but then cancelAndUnbindLocked was // called before it's run yet). This is a safe state at the moment, // since it will eventually, but feels like a source for confusion down the road and // may cause some expensive and unnecessary work to be done. userState.mConnection.cleanupService(); mUserStates.remove(userState.mUserId); mConnection.cleanupService(); } } /** Binds to the system's AttentionService which provides an actual implementation. */ @GuardedBy("mLock") private void bindLocked() { // No need to bind if service is binding or has already been bound. if (mBinding || mService != null) { return; } mBinding = true; // mContext.bindServiceAsUser() calls into ActivityManagerService which it may already // hold the lock and had called into PowerManagerService, which holds a lock. // That would create a deadlock. To solve that, putting it on a handler. mAttentionHandler.post(() -> { final Intent serviceIntent = new Intent( AttentionService.SERVICE_INTERFACE).setComponent( mComponentName); // Note: no reason to clear the calling identity, we won't have one in a handler. mContext.bindServiceAsUser(serviceIntent, mConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT); }); } /** Loading @@ -740,7 +655,7 @@ public class AttentionManagerService extends SystemService { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { cancelAndUnbindLocked(peekCurrentUserStateLocked()); cancelAndUnbindLocked(); } } } Loading Loading @@ -853,7 +768,6 @@ public class AttentionManagerService extends SystemService { private void resetStates() { mComponentName = resolveAttentionService(mContext); mUserStates.clear(); } @Override Loading
services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java +7 −33 Original line number Diff line number Diff line Loading @@ -29,7 +29,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.attention.AttentionManagerInternal.AttentionCallbackInternal; Loading @@ -50,7 +49,6 @@ 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; import org.junit.Before; import org.junit.Test; Loading @@ -64,15 +62,12 @@ import org.mockito.MockitoAnnotations; @SmallTest public class AttentionManagerServiceTest { private AttentionManagerService mSpyAttentionManager; private UserState mSpyUserState; private final int mTimeout = 1000; @Mock private AttentionCallbackInternal mMockAttentionCallbackInternal; @Mock private AttentionHandler mMockHandler; @Mock private UserState mMockUserState; @Mock private IPowerManager mMockIPowerManager; @Mock private IThermalService mMockIThermalService; Loading @@ -91,44 +86,31 @@ public class AttentionManagerServiceTest { Object mLock = new Object(); // setup a spy on attention manager AttentionManagerService mAttentionManager = new AttentionManagerService( AttentionManagerService attentionManager = new AttentionManagerService( mContext, mPowerManager, mLock, mMockHandler); mSpyAttentionManager = Mockito.spy(mAttentionManager); mSpyAttentionManager = Mockito.spy(attentionManager); // setup a spy on user state ComponentName componentName = new ComponentName("a", "b"); mSpyAttentionManager.mComponentName = componentName; UserState mUserState = new UserState(0, mContext, mLock, mMockHandler, componentName); mUserState.mService = new MockIAttentionService(); mSpyUserState = spy(mUserState); } @Test public void testCancelAttentionCheck_noCrashWhenNoUserStateLocked() { mSpyAttentionManager.cancelAttentionCheck(null); AttentionCheck attentionCheck = new AttentionCheck(mMockAttentionCallbackInternal, mSpyAttentionManager); mSpyAttentionManager.mCurrentAttentionCheck = attentionCheck; } @Test public void testCancelAttentionCheck_noCrashWhenCallbackMismatched() { mSpyUserState.mCurrentAttentionCheck = new AttentionCheck(mMockAttentionCallbackInternal, mMockUserState); doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked(); assertThat(mMockAttentionCallbackInternal).isNotNull(); mSpyAttentionManager.cancelAttentionCheck(null); } @Test public void testCancelAttentionCheck_cancelCallbackWhenMatched() { mSpyUserState.mCurrentAttentionCheck = new AttentionCheck(mMockAttentionCallbackInternal, mMockUserState); doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked(); mSpyAttentionManager.cancelAttentionCheck(mMockAttentionCallbackInternal); verify(mSpyAttentionManager).cancel(any()); verify(mSpyAttentionManager).cancel(); } @Test Loading @@ -142,7 +124,6 @@ public class AttentionManagerServiceTest { public void testCheckAttention_callOnSuccess() throws RemoteException { doReturn(true).when(mSpyAttentionManager).isServiceEnabled(); doReturn(true).when(mMockIPowerManager).isInteractive(); doReturn(mSpyUserState).when(mSpyAttentionManager).getOrCreateCurrentUserStateLocked(); doNothing().when(mSpyAttentionManager).freeIfInactiveLocked(); AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class); Loading @@ -150,13 +131,6 @@ public class AttentionManagerServiceTest { verify(callback).onSuccess(anyInt(), anyLong()); } @Test public void testOnSwitchUser_noCrashCurrentServiceIsNull() { final int userId = 10; mSpyAttentionManager.getOrCreateUserStateLocked(userId); mSpyAttentionManager.onSwitchUser(userId); } @Test public void testAttentionCheckCacheBuffer_getLast_returnTheLastElement() { AttentionCheckCacheBuffer buffer = new AttentionCheckCacheBuffer(); Loading