1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.attention;
18 
19 import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
20 
21 import static com.android.server.attention.AttentionManagerService.ATTENTION_CACHE_BUFFER_SIZE;
22 import static com.android.server.attention.AttentionManagerService.DEFAULT_STALE_AFTER_MILLIS;
23 import static com.android.server.attention.AttentionManagerService.KEY_STALE_AFTER_MILLIS;
24 
25 import static com.google.common.truth.Truth.assertThat;
26 
27 import static junit.framework.Assert.fail;
28 
29 import static org.mockito.ArgumentMatchers.any;
30 import static org.mockito.ArgumentMatchers.anyInt;
31 import static org.mockito.ArgumentMatchers.anyLong;
32 import static org.mockito.Mockito.doNothing;
33 import static org.mockito.Mockito.doReturn;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.verifyNoMoreInteractions;
37 import static org.mockito.Mockito.verifyZeroInteractions;
38 
39 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
40 import android.attention.AttentionManagerInternal.ProximityUpdateCallbackInternal;
41 import android.content.ComponentName;
42 import android.content.Context;
43 import android.os.IBinder;
44 import android.os.IPowerManager;
45 import android.os.IThermalService;
46 import android.os.PowerManager;
47 import android.os.RemoteException;
48 import android.provider.DeviceConfig;
49 import android.service.attention.IAttentionCallback;
50 import android.service.attention.IAttentionService;
51 import android.service.attention.IProximityUpdateCallback;
52 
53 import androidx.test.filters.SmallTest;
54 
55 import com.android.server.attention.AttentionManagerService.AttentionCheck;
56 import com.android.server.attention.AttentionManagerService.AttentionCheckCache;
57 import com.android.server.attention.AttentionManagerService.AttentionCheckCacheBuffer;
58 import com.android.server.attention.AttentionManagerService.AttentionHandler;
59 import com.android.server.attention.AttentionManagerService.ProximityUpdate;
60 
61 import org.junit.Before;
62 import org.junit.Test;
63 import org.mockito.Mock;
64 import org.mockito.Mockito;
65 import org.mockito.MockitoAnnotations;
66 
67 /**
68  * Tests for {@link com.android.server.attention.AttentionManagerService}
69  */
70 @SuppressWarnings("GuardedBy")
71 @SmallTest
72 public class AttentionManagerServiceTest {
73     private static final double PROXIMITY_SUCCESS_STATE = 1.0;
74     private AttentionManagerService mSpyAttentionManager;
75     private final int mTimeout = 1000;
76     private final Object mLock = new Object();
77     @Mock
78     private AttentionCallbackInternal mMockAttentionCallbackInternal;
79     @Mock
80     private AttentionHandler mMockHandler;
81     @Mock
82     private IPowerManager mMockIPowerManager;
83     @Mock
84     private IThermalService mMockIThermalService;
85     @Mock
86     Context mContext;
87     @Mock
88     private ProximityUpdateCallbackInternal mMockProximityUpdateCallbackInternal;
89 
90     @Before
setUp()91     public void setUp() throws RemoteException {
92         MockitoAnnotations.initMocks(this);
93         // setup context mock
94         doReturn(true).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any());
95         // setup power manager mock
96         PowerManager mPowerManager;
97         doReturn(true).when(mMockIPowerManager).isInteractive();
98         mPowerManager = new PowerManager(mContext, mMockIPowerManager, mMockIThermalService, null);
99 
100         // setup a spy on attention manager
101         AttentionManagerService attentionManager = new AttentionManagerService(
102                 mContext,
103                 mPowerManager,
104                 mLock,
105                 mMockHandler);
106         mSpyAttentionManager = Mockito.spy(attentionManager);
107         // setup a spy on user state
108         ComponentName componentName = new ComponentName("a", "b");
109         mSpyAttentionManager.mComponentName = componentName;
110 
111         AttentionCheck attentionCheck = new AttentionCheck(mMockAttentionCallbackInternal,
112                 mSpyAttentionManager);
113         mSpyAttentionManager.mCurrentAttentionCheck = attentionCheck;
114         mSpyAttentionManager.mService = new MockIAttentionService();
115         doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
116     }
117 
118     @Test
testRegisterProximityUpdates_returnFalseWhenServiceDisabled()119     public void testRegisterProximityUpdates_returnFalseWhenServiceDisabled() {
120         mSpyAttentionManager.mIsServiceEnabled = false;
121 
122         assertThat(mSpyAttentionManager.onStartProximityUpdates(
123                 mMockProximityUpdateCallbackInternal))
124                 .isFalse();
125     }
126 
127     @Test
testRegisterProximityUpdates_returnFalseWhenServiceUnavailable()128     public void testRegisterProximityUpdates_returnFalseWhenServiceUnavailable() {
129         mSpyAttentionManager.mIsServiceEnabled = true;
130         doReturn(false).when(mSpyAttentionManager).isServiceAvailable();
131 
132         assertThat(mSpyAttentionManager.onStartProximityUpdates(
133                 mMockProximityUpdateCallbackInternal))
134                 .isFalse();
135     }
136 
137     @Test
testRegisterProximityUpdates_returnFalseWhenPowerManagerNotInteract()138     public void testRegisterProximityUpdates_returnFalseWhenPowerManagerNotInteract()
139             throws RemoteException {
140         mSpyAttentionManager.mIsServiceEnabled = true;
141         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
142         doReturn(false).when(mMockIPowerManager).isInteractive();
143 
144         assertThat(mSpyAttentionManager.onStartProximityUpdates(
145                 mMockProximityUpdateCallbackInternal))
146                 .isFalse();
147     }
148 
149     @Test
testRegisterProximityUpdates_callOnSuccess()150     public void testRegisterProximityUpdates_callOnSuccess() throws RemoteException {
151         mSpyAttentionManager.mIsServiceEnabled = true;
152         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
153         doReturn(true).when(mMockIPowerManager).isInteractive();
154 
155         assertThat(mSpyAttentionManager.onStartProximityUpdates(
156                 mMockProximityUpdateCallbackInternal))
157                 .isTrue();
158         verify(mMockProximityUpdateCallbackInternal, times(1))
159                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
160     }
161 
162     @Test
testRegisterProximityUpdates_callOnSuccessTwiceInARow()163     public void testRegisterProximityUpdates_callOnSuccessTwiceInARow() throws RemoteException {
164         mSpyAttentionManager.mIsServiceEnabled = true;
165         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
166         doReturn(true).when(mMockIPowerManager).isInteractive();
167 
168         assertThat(mSpyAttentionManager.onStartProximityUpdates(
169                 mMockProximityUpdateCallbackInternal))
170                 .isTrue();
171 
172         ProximityUpdate prevProximityUpdate = mSpyAttentionManager.mCurrentProximityUpdate;
173         assertThat(mSpyAttentionManager.onStartProximityUpdates(
174                 mMockProximityUpdateCallbackInternal))
175                 .isTrue();
176         assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isEqualTo(prevProximityUpdate);
177         verify(mMockProximityUpdateCallbackInternal, times(1))
178                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
179     }
180 
181     @Test
testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered()182     public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() {
183         mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
184         verifyZeroInteractions(mMockProximityUpdateCallbackInternal);
185     }
186 
187     @Test
testUnregisterProximityUpdates_noCrashWhenCallbackMismatched()188     public void testUnregisterProximityUpdates_noCrashWhenCallbackMismatched()
189             throws RemoteException {
190         mSpyAttentionManager.mIsServiceEnabled = true;
191         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
192         doReturn(true).when(mMockIPowerManager).isInteractive();
193         mSpyAttentionManager.onStartProximityUpdates(mMockProximityUpdateCallbackInternal);
194         verify(mMockProximityUpdateCallbackInternal, times(1))
195                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
196 
197         ProximityUpdateCallbackInternal mismatchedCallback = new ProximityUpdateCallbackInternal() {
198             @Override
199             public void onProximityUpdate(double distance) {
200                 fail("Callback shouldn't have responded.");
201             }
202         };
203         mSpyAttentionManager.onStopProximityUpdates(mismatchedCallback);
204 
205         verifyNoMoreInteractions(mMockProximityUpdateCallbackInternal);
206     }
207 
208     @Test
testUnregisterProximityUpdates_cancelRegistrationWhenMatched()209     public void testUnregisterProximityUpdates_cancelRegistrationWhenMatched()
210             throws RemoteException {
211         mSpyAttentionManager.mIsServiceEnabled = true;
212         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
213         doReturn(true).when(mMockIPowerManager).isInteractive();
214         mSpyAttentionManager.onStartProximityUpdates(mMockProximityUpdateCallbackInternal);
215         mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
216 
217         assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isNull();
218     }
219 
220     @Test
testUnregisterProximityUpdates_noCrashWhenTwiceInARow()221     public void testUnregisterProximityUpdates_noCrashWhenTwiceInARow() throws RemoteException {
222         // Attention Service registers proximity updates.
223         mSpyAttentionManager.mIsServiceEnabled = true;
224         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
225         doReturn(true).when(mMockIPowerManager).isInteractive();
226         mSpyAttentionManager.onStartProximityUpdates(mMockProximityUpdateCallbackInternal);
227         verify(mMockProximityUpdateCallbackInternal, times(1))
228                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
229 
230         // Attention Service unregisters the proximity update twice in a row.
231         mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
232         mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
233         verifyNoMoreInteractions(mMockProximityUpdateCallbackInternal);
234     }
235 
236     @Test
testCancelAttentionCheck_noCrashWhenCallbackMismatched()237     public void testCancelAttentionCheck_noCrashWhenCallbackMismatched() {
238         assertThat(mMockAttentionCallbackInternal).isNotNull();
239         mSpyAttentionManager.cancelAttentionCheck(null);
240     }
241 
242     @Test
testCancelAttentionCheck_cancelCallbackWhenMatched()243     public void testCancelAttentionCheck_cancelCallbackWhenMatched() {
244         mSpyAttentionManager.cancelAttentionCheck(mMockAttentionCallbackInternal);
245         verify(mSpyAttentionManager).cancel();
246     }
247 
248     @Test
testCheckAttention_returnFalseWhenPowerManagerNotInteract()249     public void testCheckAttention_returnFalseWhenPowerManagerNotInteract() throws RemoteException {
250         mSpyAttentionManager.mIsServiceEnabled = true;
251         doReturn(false).when(mMockIPowerManager).isInteractive();
252         AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
253         assertThat(mSpyAttentionManager.checkAttention(mTimeout, callback)).isFalse();
254     }
255 
256     @Test
testCheckAttention_callOnSuccess()257     public void testCheckAttention_callOnSuccess() throws RemoteException {
258         mSpyAttentionManager.mIsServiceEnabled = true;
259         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
260         doReturn(true).when(mMockIPowerManager).isInteractive();
261         mSpyAttentionManager.mCurrentAttentionCheck = null;
262 
263         AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
264         mSpyAttentionManager.checkAttention(mTimeout, callback);
265         verify(callback).onSuccess(anyInt(), anyLong());
266     }
267 
268     @Test
testAttentionCheckCacheBuffer_getLast_returnTheLastElement()269     public void testAttentionCheckCacheBuffer_getLast_returnTheLastElement() {
270         AttentionCheckCacheBuffer buffer = new AttentionCheckCacheBuffer();
271         buffer.add(new AttentionCheckCache(0, 0, 1L));
272         AttentionCheckCache cache = new AttentionCheckCache(0, 0, 2L);
273         buffer.add(cache);
274         assertThat(buffer.getLast()).isEqualTo(cache);
275     }
276 
277     @Test
testAttentionCheckCacheBuffer_get_returnNullWhenOutOfBoundary()278     public void testAttentionCheckCacheBuffer_get_returnNullWhenOutOfBoundary() {
279         AttentionCheckCacheBuffer buffer = new AttentionCheckCacheBuffer();
280         assertThat(buffer.get(1)).isNull();
281     }
282 
283     @Test
testAttentionCheckCacheBuffer_get_handleCircularIndexing()284     public void testAttentionCheckCacheBuffer_get_handleCircularIndexing() {
285         AttentionCheckCacheBuffer buffer = new AttentionCheckCacheBuffer();
286         AttentionCheckCache cache = new AttentionCheckCache(0L, 0, 1L);
287         // Insert SIZE+1 elements.
288         for (int i = 0; i <= ATTENTION_CACHE_BUFFER_SIZE; i++) {
289             if (i == 1) {
290                 buffer.add(cache);
291             } else {
292                 buffer.add(new AttentionCheckCache(0L, 0, i));
293             }
294         }
295         // The element that was at index 1 should be at index 0 after inserting SIZE + 1 elements.
296         assertThat(buffer.get(0)).isEqualTo(cache);
297     }
298 
299     @Test
testGetStaleAfterMillis_handlesGoodFlagValue()300     public void testGetStaleAfterMillis_handlesGoodFlagValue() {
301         DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
302                 KEY_STALE_AFTER_MILLIS, "123", false);
303         assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(123);
304     }
305 
306     @Test
testGetStaleAfterMillis_handlesBadFlagValue_1()307     public void testGetStaleAfterMillis_handlesBadFlagValue_1() {
308         DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
309                 KEY_STALE_AFTER_MILLIS, "-123", false);
310         assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
311                 DEFAULT_STALE_AFTER_MILLIS);
312     }
313 
314     @Test
testGetStaleAfterMillis_handlesBadFlagValue_2()315     public void testGetStaleAfterMillis_handlesBadFlagValue_2() {
316         DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
317                 KEY_STALE_AFTER_MILLIS, "15000", false);
318         assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
319                 DEFAULT_STALE_AFTER_MILLIS);
320     }
321 
322     @Test
testGetStaleAfterMillis_handlesBadFlagValue_3()323     public void testGetStaleAfterMillis_handlesBadFlagValue_3() {
324         DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
325                 KEY_STALE_AFTER_MILLIS, "abracadabra", false);
326         assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
327                 DEFAULT_STALE_AFTER_MILLIS);
328     }
329 
330     @Test
testGetStaleAfterMillis_handlesBadFlagValue_4()331     public void testGetStaleAfterMillis_handlesBadFlagValue_4() {
332         DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
333                 KEY_STALE_AFTER_MILLIS, "15_000L", false);
334         assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
335                 DEFAULT_STALE_AFTER_MILLIS);
336     }
337 
338     private class MockIAttentionService implements IAttentionService {
checkAttention(IAttentionCallback callback)339         public void checkAttention(IAttentionCallback callback) throws RemoteException {
340             callback.onSuccess(0, 0);
341         }
342 
cancelAttentionCheck(IAttentionCallback callback)343         public void cancelAttentionCheck(IAttentionCallback callback) {
344         }
345 
onStartProximityUpdates(IProximityUpdateCallback callback)346         public void onStartProximityUpdates(IProximityUpdateCallback callback)
347                 throws RemoteException {
348             callback.onProximityUpdate(PROXIMITY_SUCCESS_STATE);
349         }
350 
onStopProximityUpdates()351         public void onStopProximityUpdates() throws RemoteException {
352         }
353 
asBinder()354         public IBinder asBinder() {
355             return null;
356         }
357     }
358 }
359