Added DisplayPowerProximityStateController tests

Bug: 258053532
Test: atest DisplayPowerProximityStateControllerTest

Change-Id: I04485703b6df6c3037c4f17d5f04422f97382591
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index c06101f..270aa2f 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -2691,7 +2691,7 @@
                 int displayId, SensorManager sensorManager) {
             return new DisplayPowerProximityStateController(wakelockController, displayDeviceConfig,
                     looper, nudgeUpdatePowerState,
-                    displayId, sensorManager);
+                    displayId, sensorManager, /* injector= */ null);
         }
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
index 5b64dd5..a3433d9 100644
--- a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
@@ -30,6 +30,7 @@
 import android.view.Display;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.display.utils.SensorUtils;
 
 import java.io.PrintWriter;
@@ -40,16 +41,22 @@
  * state changes.
  */
 public final class DisplayPowerProximityStateController {
-    private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 1;
+    @VisibleForTesting
+    static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 1;
+    @VisibleForTesting
+    static final int PROXIMITY_UNKNOWN = -1;
+    @VisibleForTesting
+    static final int PROXIMITY_POSITIVE = 1;
+    @VisibleForTesting
+    static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
+
     private static final int MSG_IGNORE_PROXIMITY = 2;
 
-    private static final int PROXIMITY_UNKNOWN = -1;
     private static final int PROXIMITY_NEGATIVE = 0;
-    private static final int PROXIMITY_POSITIVE = 1;
 
     private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
     // Proximity sensor debounce delay in milliseconds for positive transitions.
-    private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
+
     // Proximity sensor debounce delay in milliseconds for negative transitions.
     private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;
     // Trigger proximity if distance is less than 5 cm.
@@ -66,12 +73,13 @@
     private final DisplayPowerProximityStateHandler mHandler;
     // A runnable to execute the utility to update the power state.
     private final Runnable mNudgeUpdatePowerState;
+    private Clock mClock;
     // A listener which listen's to the events emitted by the proximity sensor.
     private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
         @Override
         public void onSensorChanged(SensorEvent event) {
             if (mProximitySensorEnabled) {
-                final long time = SystemClock.uptimeMillis();
+                final long time = mClock.uptimeMillis();
                 final float distance = event.values[0];
                 boolean positive = distance >= 0.0f && distance < mProximityThreshold;
                 handleProximitySensorEvent(time, positive);
@@ -147,7 +155,12 @@
     public DisplayPowerProximityStateController(
             WakelockController wakeLockController, DisplayDeviceConfig displayDeviceConfig,
             Looper looper,
-            Runnable nudgeUpdatePowerState, int displayId, SensorManager sensorManager) {
+            Runnable nudgeUpdatePowerState, int displayId, SensorManager sensorManager,
+            Injector injector) {
+        if (injector == null) {
+            injector = new Injector();
+        }
+        mClock = injector.createClock();
         mWakelockController = wakeLockController;
         mHandler = new DisplayPowerProximityStateHandler(looper);
         mNudgeUpdatePowerState = nudgeUpdatePowerState;
@@ -239,7 +252,6 @@
                 setProximitySensorEnabled(false);
                 mWaitingForNegativeProximity = false;
             }
-
             if (mScreenOffBecauseOfProximity
                     && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) {
                 // The screen *was* off due to prox being near, but now it's "far" so lets turn
@@ -313,7 +325,7 @@
                 + mSkipRampBecauseOfProximityChangeToNegative);
     }
 
-    private void ignoreProximitySensorUntilChangedInternal() {
+    void ignoreProximitySensorUntilChangedInternal() {
         if (!mIgnoreProximityUntilChanged
                 && mProximity == PROXIMITY_POSITIVE) {
             // Only ignore if it is still reporting positive (near)
@@ -414,7 +426,7 @@
         if (mProximitySensorEnabled
                 && mPendingProximity != PROXIMITY_UNKNOWN
                 && mPendingProximityDebounceTime >= 0) {
-            final long now = SystemClock.uptimeMillis();
+            final long now = mClock.uptimeMillis();
             if (mPendingProximityDebounceTime <= now) {
                 if (mProximity != mPendingProximity) {
                     // if the status of the sensor changed, stop ignoring.
@@ -473,4 +485,66 @@
         }
     }
 
+    @VisibleForTesting
+    boolean getPendingWaitForNegativeProximityLocked() {
+        synchronized (mLock) {
+            return mPendingWaitForNegativeProximityLocked;
+        }
+    }
+
+    @VisibleForTesting
+    boolean getWaitingForNegativeProximity() {
+        return mWaitingForNegativeProximity;
+    }
+
+    @VisibleForTesting
+    boolean shouldIgnoreProximityUntilChanged() {
+        return mIgnoreProximityUntilChanged;
+    }
+
+    boolean isProximitySensorEnabled() {
+        return mProximitySensorEnabled;
+    }
+
+    @VisibleForTesting
+    Handler getHandler() {
+        return mHandler;
+    }
+
+    @VisibleForTesting
+    int getPendingProximity() {
+        return mPendingProximity;
+    }
+
+    @VisibleForTesting
+    int getProximity() {
+        return mProximity;
+    }
+
+
+    @VisibleForTesting
+    long getPendingProximityDebounceTime() {
+        return mPendingProximityDebounceTime;
+    }
+
+    @VisibleForTesting
+    SensorEventListener getProximitySensorListener() {
+        return mProximitySensorListener;
+    }
+
+    /** Functional interface for providing time. */
+    @VisibleForTesting
+    interface Clock {
+        /**
+         * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+         */
+        long uptimeMillis();
+    }
+
+    @VisibleForTesting
+    static class Injector {
+        Clock createClock() {
+            return () -> SystemClock.uptimeMillis();
+        }
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index dc49a94..4c28c51 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -144,7 +144,7 @@
                     SensorManager sensorManager) {
                 return new DisplayPowerProximityStateController(wakelockController,
                         displayDeviceConfig, looper, nudgeUpdatePowerState, displayId,
-                        sensorManager);
+                        sensorManager, /* injector= */ null);
             }
         };
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
new file mode 100644
index 0000000..6e91b24
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.test.TestLooper;
+import android.view.Display;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class DisplayPowerProximityStateControllerTest {
+    @Mock
+    WakelockController mWakelockController;
+
+    @Mock
+    DisplayDeviceConfig mDisplayDeviceConfig;
+
+    @Mock
+    Runnable mNudgeUpdatePowerState;
+
+    @Mock
+    SensorManager mSensorManager;
+
+    private Sensor mProximitySensor;
+    private OffsettableClock mClock;
+    private TestLooper mTestLooper;
+    private SensorEventListener mSensorEventListener;
+    private DisplayPowerProximityStateController mDisplayPowerProximityStateController;
+
+    @Before
+    public void before() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mClock = new OffsettableClock.Stopped();
+        mTestLooper = new TestLooper(mClock::now);
+        when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
+                new DisplayDeviceConfig.SensorData() {
+                    {
+                        type = Sensor.STRING_TYPE_PROXIMITY;
+                        // This is kept null because currently there is no way to define a sensor
+                        // name in TestUtils
+                        name = null;
+                    }
+                });
+        setUpProxSensor();
+        DisplayPowerProximityStateController.Injector injector =
+                new DisplayPowerProximityStateController.Injector() {
+                    @Override
+                    DisplayPowerProximityStateController.Clock createClock() {
+                        return new DisplayPowerProximityStateController.Clock() {
+                            @Override
+                            public long uptimeMillis() {
+                                return mClock.now();
+                            }
+                        };
+                    }
+                };
+        mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
+                mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
+                mNudgeUpdatePowerState, 0,
+                mSensorManager, injector);
+        mSensorEventListener = mDisplayPowerProximityStateController.getProximitySensorListener();
+    }
+
+    @Test
+    public void updatePendingProximityRequestsWorksAsExpectedWhenPending() {
+        // Set the system to pending wait for proximity
+        assertTrue(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
+                true));
+        assertTrue(
+                mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
+
+        // Update the pending proximity wait request
+        mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
+        assertTrue(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
+        assertFalse(
+                mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
+    }
+
+    @Test
+    public void updatePendingProximityRequestsWorksAsExpectedWhenNotPending() {
+        // Will not wait or be in the pending wait state of not already pending
+        mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
+        assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
+        assertFalse(
+                mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
+    }
+
+    @Test
+    public void updatePendingProximityRequestsWorksAsExpectedWhenPendingAndProximityIgnored()
+            throws Exception {
+        // Set the system to the state where it will ignore proximity unless changed
+        enableProximitySensor();
+        emitAndValidatePositiveProximityEvent();
+        mDisplayPowerProximityStateController.ignoreProximitySensorUntilChangedInternal();
+        advanceTime(1);
+        assertTrue(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
+        verify(mNudgeUpdatePowerState, times(2)).run();
+
+        // Do not set the system to pending wait for proximity
+        mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
+        assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
+        assertFalse(
+                mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
+
+        // Set the system to pending wait for proximity. But because the proximity is being
+        // ignored, it will not wait or not set the pending wait
+        assertTrue(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
+                true));
+        mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
+        assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
+        assertFalse(
+                mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
+    }
+
+    @Test
+    public void cleanupDisablesTheProximitySensor() {
+        enableProximitySensor();
+        mDisplayPowerProximityStateController.cleanup();
+        verify(mSensorManager).unregisterListener(
+                mSensorEventListener);
+        assertFalse(mDisplayPowerProximityStateController.isProximitySensorEnabled());
+        assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
+        assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
+        assertEquals(mDisplayPowerProximityStateController.getProximity(),
+                DisplayPowerProximityStateController.PROXIMITY_UNKNOWN);
+        when(mWakelockController.releaseWakelock(
+                WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
+        assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
+    }
+
+    @Test
+    public void isProximitySensorAvailableReturnsTrueWhenAvailable() {
+        assertTrue(mDisplayPowerProximityStateController.isProximitySensorAvailable());
+    }
+
+    @Test
+    public void isProximitySensorAvailableReturnsFalseWhenNotAvailable() {
+        when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
+                new DisplayDeviceConfig.SensorData() {
+                    {
+                        type = null;
+                        name = null;
+                    }
+                });
+        mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
+                mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
+                mNudgeUpdatePowerState, 1,
+                mSensorManager, null);
+        assertFalse(mDisplayPowerProximityStateController.isProximitySensorAvailable());
+    }
+
+    @Test
+    public void notifyDisplayDeviceChangedReloadsTheProximitySensor() throws Exception {
+        DisplayDeviceConfig updatedDisplayDeviceConfig = mock(DisplayDeviceConfig.class);
+        when(updatedDisplayDeviceConfig.getProximitySensor()).thenReturn(
+                new DisplayDeviceConfig.SensorData() {
+                    {
+                        type = Sensor.STRING_TYPE_PROXIMITY;
+                        name = null;
+                    }
+                });
+        Sensor newProxSensor = TestUtils.createSensor(
+                Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, 4.0f);
+        when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL)))
+                .thenReturn(List.of(newProxSensor));
+        mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(
+                updatedDisplayDeviceConfig);
+        assertTrue(mDisplayPowerProximityStateController.isProximitySensorAvailable());
+    }
+
+    @Test
+    public void setPendingWaitForNegativeProximityLockedWorksAsExpected() {
+        // Doesn't do anything not asked to wait
+        assertFalse(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
+                false));
+        assertFalse(
+                mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
+
+        // Sets pending wait negative proximity if not already waiting
+        assertTrue(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
+                true));
+        assertTrue(
+                mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
+
+        // Will not set pending wait negative proximity if already waiting
+        assertFalse(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
+                true));
+        assertTrue(
+                mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
+
+    }
+
+    @Test
+    public void evaluateProximityStateWhenRequestedUseOfProximitySensor() throws Exception {
+        // Enable the proximity sensor
+        enableProximitySensor();
+
+        // Emit a positive proximity event to move the system to a state to mimic a scenario
+        // where the system is in positive proximity
+        emitAndValidatePositiveProximityEvent();
+
+        // Again evaluate the proximity state, with system having positive proximity
+        setScreenOffBecauseOfPositiveProximityState();
+    }
+
+    @Test
+    public void evaluateProximityStateWhenScreenOffBecauseOfPositiveProximity() throws Exception {
+        // Enable the proximity sensor
+        enableProximitySensor();
+
+        // Emit a positive proximity event to move the system to a state to mimic a scenario
+        // where the system is in positive proximity
+        emitAndValidatePositiveProximityEvent();
+
+        // Again evaluate the proximity state, with system having positive proximity
+        setScreenOffBecauseOfPositiveProximityState();
+
+        // Set the system to pending wait for proximity
+        mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(true);
+        // Update the pending proximity wait request
+        mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
+
+        // Start ignoring proximity sensor
+        mDisplayPowerProximityStateController.ignoreProximitySensorUntilChangedInternal();
+        // Re-evaluate the proximity state, such that the system is detecting the positive
+        // proximity, and screen is off because of that
+        when(mWakelockController.getOnProximityNegativeRunnable()).thenReturn(mock(Runnable.class));
+        mDisplayPowerProximityStateController.updateProximityState(mock(
+                DisplayManagerInternal.DisplayPowerRequest.class), Display.STATE_ON);
+        assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
+        assertFalse(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
+        assertTrue(
+                mDisplayPowerProximityStateController
+                        .shouldSkipRampBecauseOfProximityChangeToNegative());
+        verify(mWakelockController).acquireWakelock(
+                WakelockController.WAKE_LOCK_PROXIMITY_NEGATIVE);
+    }
+
+    @Test
+    public void evaluateProximityStateWhenDisplayIsTurningOff() throws Exception {
+        // Enable the proximity sensor
+        enableProximitySensor();
+
+        // Emit a positive proximity event to move the system to a state to mimic a scenario
+        // where the system is in positive proximity
+        emitAndValidatePositiveProximityEvent();
+
+        // Again evaluate the proximity state, with system having positive proximity
+        setScreenOffBecauseOfPositiveProximityState();
+
+        // Re-evaluate the proximity state, such that the system is detecting the positive
+        // proximity, and screen is off because of that
+        mDisplayPowerProximityStateController.updateProximityState(mock(
+                DisplayManagerInternal.DisplayPowerRequest.class), Display.STATE_OFF);
+        verify(mSensorManager).unregisterListener(
+                mSensorEventListener);
+        assertFalse(mDisplayPowerProximityStateController.isProximitySensorEnabled());
+        assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
+        assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
+        assertEquals(mDisplayPowerProximityStateController.getProximity(),
+                DisplayPowerProximityStateController.PROXIMITY_UNKNOWN);
+        when(mWakelockController.releaseWakelock(
+                WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
+        assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
+    }
+
+    @Test
+    public void evaluateProximityStateNotWaitingForNegativeProximityAndNotUsingProxSensor()
+            throws Exception {
+        // Enable the proximity sensor
+        enableProximitySensor();
+
+        // Emit a positive proximity event to move the system to a state to mimic a scenario
+        // where the system is in positive proximity
+        emitAndValidatePositiveProximityEvent();
+
+        // Re-evaluate the proximity state, such that the system is detecting the positive
+        // proximity, and screen is off because of that
+        mDisplayPowerProximityStateController.updateProximityState(mock(
+                DisplayManagerInternal.DisplayPowerRequest.class), Display.STATE_ON);
+        verify(mSensorManager).unregisterListener(
+                mSensorEventListener);
+        assertFalse(mDisplayPowerProximityStateController.isProximitySensorEnabled());
+        assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
+        assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
+        assertEquals(mDisplayPowerProximityStateController.getProximity(),
+                DisplayPowerProximityStateController.PROXIMITY_UNKNOWN);
+        when(mWakelockController.releaseWakelock(
+                WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
+        assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
+    }
+
+    private void advanceTime(long timeMs) {
+        mClock.fastForward(timeMs);
+        mTestLooper.dispatchAll();
+    }
+
+    private void setUpProxSensor() throws Exception {
+        mProximitySensor = TestUtils.createSensor(
+                Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, 5.0f);
+        when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL)))
+                .thenReturn(List.of(mProximitySensor));
+    }
+
+    private void emitAndValidatePositiveProximityEvent() throws Exception {
+        // Emit a positive proximity event to move the system to a state to mimic a scenario
+        // where the system is in positive proximity
+        when(mWakelockController.releaseWakelock(
+                WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
+        mSensorEventListener.onSensorChanged(TestUtils.createSensorEvent(mProximitySensor, 4));
+        verify(mSensorManager).registerListener(mSensorEventListener,
+                mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL,
+                mDisplayPowerProximityStateController.getHandler());
+        verify(mWakelockController).acquireWakelock(
+                WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
+        assertEquals(mDisplayPowerProximityStateController.getPendingProximity(),
+                DisplayPowerProximityStateController.PROXIMITY_POSITIVE);
+        assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
+        assertEquals(mDisplayPowerProximityStateController.getProximity(),
+                DisplayPowerProximityStateController.PROXIMITY_POSITIVE);
+        verify(mNudgeUpdatePowerState).run();
+        assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
+    }
+
+    // Call evaluateProximityState with the request for using the proximity sensor. This will
+    // register the proximity sensor listener, which will be needed for mocking positive
+    // proximity scenarios.
+    private void enableProximitySensor() {
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+                DisplayManagerInternal.DisplayPowerRequest.class);
+        displayPowerRequest.useProximitySensor = true;
+        mDisplayPowerProximityStateController.updateProximityState(displayPowerRequest,
+                Display.STATE_ON);
+        verify(mSensorManager).registerListener(
+                mSensorEventListener,
+                mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL,
+                mDisplayPowerProximityStateController.getHandler());
+        assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
+        assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
+        assertFalse(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
+        verifyZeroInteractions(mWakelockController);
+    }
+
+    private void setScreenOffBecauseOfPositiveProximityState() {
+        // Prepare a request to indicate that the proximity sensor is to be used
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+                DisplayManagerInternal.DisplayPowerRequest.class);
+        displayPowerRequest.useProximitySensor = true;
+
+        Runnable onProximityPositiveRunnable = mock(Runnable.class);
+        when(mWakelockController.getOnProximityPositiveRunnable()).thenReturn(
+                onProximityPositiveRunnable);
+
+        mDisplayPowerProximityStateController.updateProximityState(displayPowerRequest,
+                Display.STATE_ON);
+        verify(mSensorManager).registerListener(
+                mSensorEventListener,
+                mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL,
+                mDisplayPowerProximityStateController.getHandler());
+        assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
+        assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
+        assertTrue(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
+        verify(mWakelockController).acquireWakelock(
+                WakelockController.WAKE_LOCK_PROXIMITY_POSITIVE);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/TestUtils.java b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
index 0454587..a419b3f 100644
--- a/services/tests/servicestests/src/com/android/server/display/TestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
@@ -51,6 +51,12 @@
         }
     }
 
+    public static void setMaximumRange(Sensor sensor, float maximumRange) throws Exception {
+        Method setter = Sensor.class.getDeclaredMethod("setRange", Float.TYPE, Float.TYPE);
+        setter.setAccessible(true);
+        setter.invoke(sensor, maximumRange, 1);
+    }
+
     public static Sensor createSensor(int type, String strType) throws Exception {
         Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
         constr.setAccessible(true);
@@ -59,6 +65,16 @@
         return sensor;
     }
 
+    public static Sensor createSensor(int type, String strType, float maximumRange)
+            throws Exception {
+        Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+        constr.setAccessible(true);
+        Sensor sensor = constr.newInstance();
+        setSensorType(sensor, type, strType);
+        setMaximumRange(sensor, maximumRange);
+        return sensor;
+    }
+
     /**
      * Create a custom {@link DisplayAddress} to ensure we're not relying on any specific
      * display-address implementation in our code. Intentionally uses default object (reference)