1 /*
2  * Copyright (C) 2022 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.display;
18 
19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verifyZeroInteractions;
28 import static org.mockito.Mockito.when;
29 
30 import android.hardware.Sensor;
31 import android.hardware.SensorEventListener;
32 import android.hardware.SensorManager;
33 import android.hardware.display.DisplayManagerInternal;
34 import android.os.test.TestLooper;
35 import android.view.Display;
36 
37 import androidx.test.ext.junit.runners.AndroidJUnit4;
38 import androidx.test.filters.SmallTest;
39 
40 import com.android.server.testutils.OffsettableClock;
41 
42 import org.junit.Before;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 import org.mockito.Mock;
46 import org.mockito.MockitoAnnotations;
47 
48 import java.util.List;
49 
50 @SmallTest
51 @RunWith(AndroidJUnit4.class)
52 public final class DisplayPowerProximityStateControllerTest {
53     @Mock
54     WakelockController mWakelockController;
55 
56     @Mock
57     DisplayDeviceConfig mDisplayDeviceConfig;
58 
59     @Mock
60     Runnable mNudgeUpdatePowerState;
61 
62     @Mock
63     SensorManager mSensorManager;
64 
65     private Sensor mProximitySensor;
66     private OffsettableClock mClock;
67     private TestLooper mTestLooper;
68     private SensorEventListener mSensorEventListener;
69     private DisplayPowerProximityStateController mDisplayPowerProximityStateController;
70 
71     @Before
before()72     public void before() throws Exception {
73         MockitoAnnotations.initMocks(this);
74         mClock = new OffsettableClock.Stopped();
75         mTestLooper = new TestLooper(mClock::now);
76         when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
77                 new DisplayDeviceConfig.SensorData() {
78                     {
79                         type = Sensor.STRING_TYPE_PROXIMITY;
80                         // This is kept null because currently there is no way to define a sensor
81                         // name in TestUtils
82                         name = null;
83                     }
84                 });
85         setUpProxSensor();
86         DisplayPowerProximityStateController.Injector injector =
87                 new DisplayPowerProximityStateController.Injector() {
88                     @Override
89                     DisplayPowerProximityStateController.Clock createClock() {
90                         return mClock::now;
91                     }
92                 };
93         mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
94                 mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
95                 mNudgeUpdatePowerState, Display.DEFAULT_DISPLAY,
96                 mSensorManager, injector);
97         mSensorEventListener = mDisplayPowerProximityStateController.getProximitySensorListener();
98     }
99 
100     @Test
updatePendingProximityRequestsWorksAsExpectedWhenPending()101     public void updatePendingProximityRequestsWorksAsExpectedWhenPending() {
102         // Set the system to pending wait for proximity
103         assertTrue(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
104                 true));
105         assertTrue(
106                 mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
107 
108         // Update the pending proximity wait request
109         mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
110         assertTrue(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
111         assertFalse(
112                 mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
113     }
114 
115     @Test
updatePendingProximityRequestsWorksAsExpectedWhenNotPending()116     public void updatePendingProximityRequestsWorksAsExpectedWhenNotPending() {
117         // Will not wait or be in the pending wait state of not already pending
118         mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
119         assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
120         assertFalse(
121                 mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
122     }
123 
124     @Test
updatePendingProximityRequestsWorksAsExpectedWhenPendingAndProximityIgnored()125     public void updatePendingProximityRequestsWorksAsExpectedWhenPendingAndProximityIgnored()
126             throws Exception {
127         // Set the system to the state where it will ignore proximity unless changed
128         enableProximitySensor();
129         emitAndValidatePositiveProximityEvent();
130         mDisplayPowerProximityStateController.ignoreProximitySensorUntilChangedInternal();
131         advanceTime();
132         assertTrue(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
133         verify(mNudgeUpdatePowerState, times(2)).run();
134 
135         // Do not set the system to pending wait for proximity
136         mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
137         assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
138         assertFalse(
139                 mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
140 
141         // Set the system to pending wait for proximity. But because the proximity is being
142         // ignored, it will not wait or not set the pending wait
143         assertTrue(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
144                 true));
145         mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
146         assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
147         assertFalse(
148                 mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
149     }
150 
151     @Test
cleanupDisablesTheProximitySensor()152     public void cleanupDisablesTheProximitySensor() {
153         enableProximitySensor();
154         mDisplayPowerProximityStateController.cleanup();
155         verify(mSensorManager).unregisterListener(
156                 mSensorEventListener);
157         assertFalse(mDisplayPowerProximityStateController.isProximitySensorEnabled());
158         assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
159         assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
160         assertEquals(mDisplayPowerProximityStateController.getProximity(),
161                 DisplayPowerProximityStateController.PROXIMITY_UNKNOWN);
162         when(mWakelockController.releaseWakelock(
163                 WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
164         assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
165     }
166 
167     @Test
isProximitySensorAvailableReturnsTrueWhenAvailable()168     public void isProximitySensorAvailableReturnsTrueWhenAvailable() {
169         assertTrue(mDisplayPowerProximityStateController.isProximitySensorAvailable());
170     }
171 
172     @Test
isProximitySensorAvailableReturnsFalseWhenNotAvailableAndNoDefault()173     public void isProximitySensorAvailableReturnsFalseWhenNotAvailableAndNoDefault() {
174         when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
175                 new DisplayDeviceConfig.SensorData() {
176                     {
177                         type = null;
178                         name = null;
179                     }
180                 });
181         mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
182                 mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
183                 mNudgeUpdatePowerState, Display.DEFAULT_DISPLAY,
184                 mSensorManager, null);
185         assertFalse(mDisplayPowerProximityStateController.isProximitySensorAvailable());
186     }
187 
188     @Test
isProximitySensorAvailableReturnsTrueWhenNotAvailableAndHasDefault()189     public void isProximitySensorAvailableReturnsTrueWhenNotAvailableAndHasDefault()
190             throws Exception {
191         when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
192                 new DisplayDeviceConfig.SensorData() {
193                     {
194                         type = null;
195                         name = null;
196                     }
197                 });
198         when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(
199                 TestUtils.createSensor(Sensor.TYPE_PROXIMITY, "proximity"));
200         mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
201                 mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
202                 mNudgeUpdatePowerState, Display.DEFAULT_DISPLAY,
203                 mSensorManager, null);
204         assertTrue(mDisplayPowerProximityStateController.isProximitySensorAvailable());
205     }
206 
207     @Test
isProximitySensorAvailableReturnsFalseWhenNotAvailableHasDefaultNonDefaultDisplay()208     public void isProximitySensorAvailableReturnsFalseWhenNotAvailableHasDefaultNonDefaultDisplay()
209             throws Exception {
210         when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
211                 new DisplayDeviceConfig.SensorData() {
212                     {
213                         type = null;
214                         name = null;
215                     }
216                 });
217         when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(
218                 TestUtils.createSensor(Sensor.TYPE_PROXIMITY, "proximity"));
219         mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
220                 mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
221                 mNudgeUpdatePowerState, 1,
222                 mSensorManager, null);
223         assertFalse(mDisplayPowerProximityStateController.isProximitySensorAvailable());
224     }
225 
226     @Test
isProximitySensorAvailableReturnsTrueWhenNoSensorConfigured()227     public void isProximitySensorAvailableReturnsTrueWhenNoSensorConfigured() throws Exception {
228         when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(null);
229         when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(
230                 TestUtils.createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY));
231 
232         mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
233                 mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
234                 mNudgeUpdatePowerState, Display.DEFAULT_DISPLAY,
235                 mSensorManager, null);
236         assertFalse(mDisplayPowerProximityStateController.isProximitySensorAvailable());
237     }
238 
239     @Test
notifyDisplayDeviceChangedReloadsTheProximitySensor()240     public void notifyDisplayDeviceChangedReloadsTheProximitySensor() throws Exception {
241         DisplayDeviceConfig updatedDisplayDeviceConfig = mock(DisplayDeviceConfig.class);
242         when(updatedDisplayDeviceConfig.getProximitySensor()).thenReturn(
243                 new DisplayDeviceConfig.SensorData() {
244                     {
245                         type = Sensor.STRING_TYPE_PROXIMITY;
246                         name = null;
247                     }
248                 });
249         Sensor newProxSensor = TestUtils.createSensor(
250                 Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, 4.0f);
251         when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL)))
252                 .thenReturn(List.of(newProxSensor));
253         mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(
254                 updatedDisplayDeviceConfig);
255         assertTrue(mDisplayPowerProximityStateController.isProximitySensorAvailable());
256     }
257 
258     @Test
setPendingWaitForNegativeProximityLockedWorksAsExpected()259     public void setPendingWaitForNegativeProximityLockedWorksAsExpected() {
260         // Doesn't do anything not asked to wait
261         assertFalse(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
262                 false));
263         assertFalse(
264                 mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
265 
266         // Sets pending wait negative proximity if not already waiting
267         assertTrue(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
268                 true));
269         assertTrue(
270                 mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
271 
272         // Will not set pending wait negative proximity if already waiting
273         assertFalse(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
274                 true));
275         assertTrue(
276                 mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
277 
278     }
279 
280     @Test
evaluateProximityStateWhenRequestedUseOfProximitySensor()281     public void evaluateProximityStateWhenRequestedUseOfProximitySensor() throws Exception {
282         // Enable the proximity sensor
283         enableProximitySensor();
284 
285         // Emit a positive proximity event to move the system to a state to mimic a scenario
286         // where the system is in positive proximity
287         emitAndValidatePositiveProximityEvent();
288 
289         // Again evaluate the proximity state, with system having positive proximity
290         setScreenOffBecauseOfPositiveProximityState();
291     }
292 
293     @Test
evaluateProximityStateWhenScreenOffBecauseOfPositiveProximity()294     public void evaluateProximityStateWhenScreenOffBecauseOfPositiveProximity() throws Exception {
295         // Enable the proximity sensor
296         enableProximitySensor();
297 
298         // Emit a positive proximity event to move the system to a state to mimic a scenario
299         // where the system is in positive proximity
300         emitAndValidatePositiveProximityEvent();
301 
302         // Again evaluate the proximity state, with system having positive proximity
303         setScreenOffBecauseOfPositiveProximityState();
304 
305         // Set the system to pending wait for proximity
306         mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(true);
307         // Update the pending proximity wait request
308         mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
309 
310         // Start ignoring proximity sensor
311         mDisplayPowerProximityStateController.ignoreProximitySensorUntilChangedInternal();
312         // Re-evaluate the proximity state, such that the system is detecting the positive
313         // proximity, and screen is off because of that
314         when(mWakelockController.getOnProximityNegativeRunnable()).thenReturn(mock(Runnable.class));
315         mDisplayPowerProximityStateController.updateProximityState(mock(
316                 DisplayManagerInternal.DisplayPowerRequest.class), Display.STATE_ON);
317         assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
318         assertFalse(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
319         assertTrue(
320                 mDisplayPowerProximityStateController
321                         .shouldSkipRampBecauseOfProximityChangeToNegative());
322         verify(mWakelockController).acquireWakelock(
323                 WakelockController.WAKE_LOCK_PROXIMITY_NEGATIVE);
324     }
325 
326     @Test
evaluateProximityStateWhenDisplayIsTurningOff()327     public void evaluateProximityStateWhenDisplayIsTurningOff() throws Exception {
328         // Enable the proximity sensor
329         enableProximitySensor();
330 
331         // Emit a positive proximity event to move the system to a state to mimic a scenario
332         // where the system is in positive proximity
333         emitAndValidatePositiveProximityEvent();
334 
335         // Again evaluate the proximity state, with system having positive proximity
336         setScreenOffBecauseOfPositiveProximityState();
337 
338         // Re-evaluate the proximity state, such that the system is detecting the positive
339         // proximity, and screen is off because of that
340         mDisplayPowerProximityStateController.updateProximityState(mock(
341                 DisplayManagerInternal.DisplayPowerRequest.class), Display.STATE_OFF);
342         verify(mSensorManager).unregisterListener(
343                 mSensorEventListener);
344         assertFalse(mDisplayPowerProximityStateController.isProximitySensorEnabled());
345         assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
346         assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
347         assertEquals(mDisplayPowerProximityStateController.getProximity(),
348                 DisplayPowerProximityStateController.PROXIMITY_UNKNOWN);
349         when(mWakelockController.releaseWakelock(
350                 WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
351         assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
352     }
353 
354     @Test
evaluateProximityStateNotWaitingForNegativeProximityAndNotUsingProxSensor()355     public void evaluateProximityStateNotWaitingForNegativeProximityAndNotUsingProxSensor()
356             throws Exception {
357         // Enable the proximity sensor
358         enableProximitySensor();
359 
360         // Emit a positive proximity event to move the system to a state to mimic a scenario
361         // where the system is in positive proximity
362         emitAndValidatePositiveProximityEvent();
363 
364         // Re-evaluate the proximity state, such that the system is detecting the positive
365         // proximity, and screen is off because of that
366         mDisplayPowerProximityStateController.updateProximityState(mock(
367                 DisplayManagerInternal.DisplayPowerRequest.class), Display.STATE_ON);
368         verify(mSensorManager).unregisterListener(
369                 mSensorEventListener);
370         assertFalse(mDisplayPowerProximityStateController.isProximitySensorEnabled());
371         assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
372         assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
373         assertEquals(mDisplayPowerProximityStateController.getProximity(),
374                 DisplayPowerProximityStateController.PROXIMITY_UNKNOWN);
375         when(mWakelockController.releaseWakelock(
376                 WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
377         assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
378     }
379 
advanceTime()380     private void advanceTime() {
381         mClock.fastForward(1);
382         mTestLooper.dispatchAll();
383     }
384 
setUpProxSensor()385     private void setUpProxSensor() throws Exception {
386         mProximitySensor = TestUtils.createSensor(
387                 Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, 5.0f);
388         when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL)))
389                 .thenReturn(List.of(mProximitySensor));
390     }
391 
emitAndValidatePositiveProximityEvent()392     private void emitAndValidatePositiveProximityEvent() throws Exception {
393         // Emit a positive proximity event to move the system to a state to mimic a scenario
394         // where the system is in positive proximity
395         when(mWakelockController.releaseWakelock(
396                 WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
397         mSensorEventListener.onSensorChanged(TestUtils.createSensorEvent(mProximitySensor, 4));
398         verify(mSensorManager).registerListener(mSensorEventListener,
399                 mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL,
400                 mDisplayPowerProximityStateController.getHandler());
401         verify(mWakelockController).acquireWakelock(
402                 WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
403         assertEquals(mDisplayPowerProximityStateController.getPendingProximity(),
404                 DisplayPowerProximityStateController.PROXIMITY_POSITIVE);
405         assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
406         assertEquals(mDisplayPowerProximityStateController.getProximity(),
407                 DisplayPowerProximityStateController.PROXIMITY_POSITIVE);
408         verify(mNudgeUpdatePowerState).run();
409         assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
410     }
411 
412     // Call evaluateProximityState with the request for using the proximity sensor. This will
413     // register the proximity sensor listener, which will be needed for mocking positive
414     // proximity scenarios.
enableProximitySensor()415     private void enableProximitySensor() {
416         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
417                 DisplayManagerInternal.DisplayPowerRequest.class);
418         displayPowerRequest.useProximitySensor = true;
419         mDisplayPowerProximityStateController.updateProximityState(displayPowerRequest,
420                 Display.STATE_ON);
421         verify(mSensorManager).registerListener(
422                 mSensorEventListener,
423                 mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL,
424                 mDisplayPowerProximityStateController.getHandler());
425         assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
426         assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
427         assertFalse(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
428         verifyZeroInteractions(mWakelockController);
429     }
430 
setScreenOffBecauseOfPositiveProximityState()431     private void setScreenOffBecauseOfPositiveProximityState() {
432         // Prepare a request to indicate that the proximity sensor is to be used
433         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
434                 DisplayManagerInternal.DisplayPowerRequest.class);
435         displayPowerRequest.useProximitySensor = true;
436 
437         Runnable onProximityPositiveRunnable = mock(Runnable.class);
438         when(mWakelockController.getOnProximityPositiveRunnable()).thenReturn(
439                 onProximityPositiveRunnable);
440 
441         mDisplayPowerProximityStateController.updateProximityState(displayPowerRequest,
442                 Display.STATE_ON);
443         verify(mSensorManager).registerListener(
444                 mSensorEventListener,
445                 mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL,
446                 mDisplayPowerProximityStateController.getHandler());
447         assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
448         assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
449         assertTrue(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
450         verify(mWakelockController).acquireWakelock(
451                 WakelockController.WAKE_LOCK_PROXIMITY_POSITIVE);
452     }
453 }
454