diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java index 411dd794d1bd7..d7d4851bf33f6 100644 --- a/services/core/java/com/android/server/attention/AttentionManagerService.java +++ b/services/core/java/com/android/server/attention/AttentionManagerService.java @@ -58,6 +58,7 @@ import android.util.StatsLog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -100,16 +101,25 @@ public class AttentionManagerService extends SystemService { private final Object mLock; @GuardedBy("mLock") private final SparseArray mUserStates = new SparseArray<>(); - private final AttentionHandler mAttentionHandler; + private AttentionHandler mAttentionHandler; - private ComponentName mComponentName; + @VisibleForTesting + ComponentName mComponentName; public AttentionManagerService(Context context) { + this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE), + new Object(), null); + mAttentionHandler = new AttentionHandler(); + } + + @VisibleForTesting + AttentionManagerService(Context context, PowerManager powerManager, Object lock, + AttentionHandler handler) { super(context); mContext = Preconditions.checkNotNull(context); - mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mLock = new Object(); - mAttentionHandler = new AttentionHandler(); + mPowerManager = powerManager; + mLock = lock; + mAttentionHandler = handler; } @Override @@ -147,7 +157,8 @@ public class AttentionManagerService extends SystemService { return isServiceEnabled() && isServiceAvailable(); } - private boolean isServiceEnabled() { + @VisibleForTesting + protected boolean isServiceEnabled() { return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED); } @@ -161,7 +172,8 @@ public class AttentionManagerService extends SystemService { * * @return {@code true} if the framework was able to dispatch the request */ - private boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) { + @VisibleForTesting + boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) { Preconditions.checkNotNull(callbackInternal); if (!isAttentionServiceSupported()) { @@ -257,24 +269,24 @@ public class AttentionManagerService extends SystemService { } /** Cancels the specified attention check. */ - private void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { + @VisibleForTesting + void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { synchronized (mLock) { final UserState userState = peekCurrentUserStateLocked(); if (userState == null) { return; } - if (!userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) { Slog.e(LOG_TAG, "Cannot cancel a non-current request"); return; } - cancel(userState); } } @GuardedBy("mLock") - private void freeIfInactiveLocked() { + @VisibleForTesting + protected void freeIfInactiveLocked() { // If we are called here, it means someone used the API again - reset the timer then. mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION); @@ -291,7 +303,8 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") - private UserState getOrCreateCurrentUserStateLocked() { + @VisibleForTesting + protected UserState getOrCreateCurrentUserStateLocked() { return getOrCreateUserStateLocked(ActivityManager.getCurrentUser()); } @@ -307,7 +320,8 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") @Nullable - private UserState peekCurrentUserStateLocked() { + @VisibleForTesting + protected UserState peekCurrentUserStateLocked() { return peekUserStateLocked(ActivityManager.getCurrentUser()); } @@ -418,7 +432,8 @@ public class AttentionManagerService extends SystemService { } } - private static final class AttentionCheck { + @VisibleForTesting + static final class AttentionCheck { private final AttentionCallbackInternal mCallbackInternal; private final IAttentionCallback mIAttentionCallback; private boolean mIsDispatched; @@ -435,7 +450,8 @@ public class AttentionManagerService extends SystemService { } } - private static final class UserState { + @VisibleForTesting + protected static class UserState { final ComponentName mComponentName; final AttentionServiceConnection mConnection = new AttentionServiceConnection(); @@ -453,7 +469,7 @@ public class AttentionManagerService extends SystemService { final Context mContext; final Object mLock; - private UserState(int userId, Context context, Object lock, ComponentName componentName) { + UserState(int userId, Context context, Object lock, ComponentName componentName) { mUserId = userId; mContext = Preconditions.checkNotNull(context); mLock = Preconditions.checkNotNull(lock); @@ -509,7 +525,7 @@ public class AttentionManagerService extends SystemService { } } - private final class AttentionServiceConnection implements ServiceConnection { + private class AttentionServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { init(IAttentionService.Stub.asInterface(service)); @@ -544,7 +560,8 @@ public class AttentionManagerService extends SystemService { } } - private class AttentionHandler extends Handler { + @VisibleForTesting + protected class AttentionHandler extends Handler { private static final int CHECK_CONNECTION_EXPIRATION = 1; private static final int ATTENTION_CHECK_TIMEOUT = 2; @@ -576,7 +593,8 @@ public class AttentionManagerService extends SystemService { } } - private void cancel(UserState userState) { + @VisibleForTesting + void cancel(UserState userState) { if (userState == null || userState.mCurrentAttentionCheck == null) { return; } diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java new file mode 100644 index 0000000000000..8426a0bb2c112 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.attention; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +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; +import android.content.ComponentName; +import android.content.Context; +import android.os.IBinder; +import android.os.IPowerManager; +import android.os.PowerManager; +import android.os.RemoteException; +import android.service.attention.IAttentionCallback; +import android.service.attention.IAttentionService; + +import androidx.test.filters.SmallTest; + +import com.android.server.attention.AttentionManagerService.AttentionCheck; +import com.android.server.attention.AttentionManagerService.AttentionHandler; +import com.android.server.attention.AttentionManagerService.UserState; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link com.android.server.attention.AttentionManagerService} + */ +@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 IAttentionCallback mMockIAttentionCallback; + @Mock private IPowerManager mMockIPowerManager; + @Mock Context mContext; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + // setup context mock + doReturn(true).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any()); + // setup power manager mock + PowerManager mPowerManager; + doReturn(true).when(mMockIPowerManager).isInteractive(); + mPowerManager = new PowerManager(mContext, mMockIPowerManager, null); + + Object mLock = new Object(); + // setup a spy on attention manager + AttentionManagerService mAttentionManager = new AttentionManagerService( + mContext, + mPowerManager, + mLock, + mMockHandler); + mSpyAttentionManager = Mockito.spy(mAttentionManager); + // setup a spy on user state + ComponentName componentName = new ComponentName("a", "b"); + mSpyAttentionManager.mComponentName = componentName; + UserState mUserState = new UserState(0, + mContext, + mLock, + componentName); + mUserState.mService = new MockIAttentionService(); + mSpyUserState = spy(mUserState); + } + + @Test + public void testCancelAttentionCheck_noCrashWhenNoUserStateLocked() { + mSpyAttentionManager.cancelAttentionCheck(null); + } + + @Test + public void testCancelAttentionCheck_noCrashWhenCallbackMismatched() { + mSpyUserState.mCurrentAttentionCheck = + new AttentionCheck(mMockAttentionCallbackInternal, mMockIAttentionCallback); + doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked(); + mSpyAttentionManager.cancelAttentionCheck(null); + } + + @Test + public void testCancelAttentionCheck_cancelCallbackWhenMatched() { + mSpyUserState.mCurrentAttentionCheck = + new AttentionCheck(mMockAttentionCallbackInternal, mMockIAttentionCallback); + doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked(); + mSpyAttentionManager.cancelAttentionCheck(mMockAttentionCallbackInternal); + verify(mSpyAttentionManager).cancel(any()); + } + + @Test + public void testCheckAttention_returnFalseWhenPowerManagerNotInteract() throws RemoteException { + doReturn(false).when(mMockIPowerManager).isInteractive(); + AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class); + assertThat(mSpyAttentionManager.checkAttention(mTimeout, callback)).isFalse(); + } + + @Test + 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); + mSpyAttentionManager.checkAttention(mTimeout, callback); + verify(callback).onSuccess(anyInt(), anyLong()); + } + + 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; + } + } +}