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