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;
private final Object mLock;
@GuardedBy("mLock")
private final SparseArray<UserState> 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
return isServiceEnabled() && isServiceAvailable();
}
- private boolean isServiceEnabled() {
+ @VisibleForTesting
+ protected boolean isServiceEnabled() {
return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, SERVICE_ENABLED,
DEFAULT_SERVICE_ENABLED);
}
*
* @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()) {
}
/** 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);
@GuardedBy("mLock")
- private UserState getOrCreateCurrentUserStateLocked() {
+ @VisibleForTesting
+ protected UserState getOrCreateCurrentUserStateLocked() {
return getOrCreateUserStateLocked(ActivityManager.getCurrentUser());
}
@GuardedBy("mLock")
@Nullable
- private UserState peekCurrentUserStateLocked() {
+ @VisibleForTesting
+ protected UserState peekCurrentUserStateLocked() {
return peekUserStateLocked(ActivityManager.getCurrentUser());
}
}
}
- private static final class AttentionCheck {
+ @VisibleForTesting
+ static final class AttentionCheck {
private final AttentionCallbackInternal mCallbackInternal;
private final IAttentionCallback mIAttentionCallback;
private boolean mIsDispatched;
}
}
- private static final class UserState {
+ @VisibleForTesting
+ protected static class UserState {
final ComponentName mComponentName;
final AttentionServiceConnection mConnection = new AttentionServiceConnection();
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);
}
}
- private final class AttentionServiceConnection implements ServiceConnection {
+ private class AttentionServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
init(IAttentionService.Stub.asInterface(service));
}
}
- 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;
}
}
- private void cancel(UserState userState) {
+ @VisibleForTesting
+ void cancel(UserState userState) {
if (userState == null || userState.mCurrentAttentionCheck == null) {
return;
}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}