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 android.hardware.Sensor;
20 import android.hardware.SensorEvent;
21 import android.hardware.SensorEventListener;
22 import android.hardware.SensorManager;
23 import android.hardware.display.DisplayManagerInternal;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.os.SystemClock;
28 import android.util.Slog;
29 import android.util.TimeUtils;
30 import android.view.Display;
31 
32 import com.android.internal.annotations.GuardedBy;
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.server.display.utils.SensorUtils;
35 
36 import java.io.PrintWriter;
37 
38 /**
39  * Maintains the proximity state of the display.
40  * Internally listens for proximity updates and schedules a power state update when the proximity
41  * state changes.
42  */
43 public final class DisplayPowerProximityStateController {
44     @VisibleForTesting
45     static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 1;
46     @VisibleForTesting
47     static final int PROXIMITY_UNKNOWN = -1;
48     @VisibleForTesting
49     static final int PROXIMITY_POSITIVE = 1;
50     @VisibleForTesting
51     static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
52 
53     private static final int MSG_IGNORE_PROXIMITY = 2;
54 
55     private static final int PROXIMITY_NEGATIVE = 0;
56 
57     private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
58     // Proximity sensor debounce delay in milliseconds for positive transitions.
59 
60     // Proximity sensor debounce delay in milliseconds for negative transitions.
61     private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;
62     // Trigger proximity if distance is less than 5 cm.
63     private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
64 
65     private final String mTag;
66     // A lock to handle the deadlock and race conditions.
67     private final Object mLock = new Object();
68     // The manager which lets us access the device's ProximitySensor
69     private final SensorManager mSensorManager;
70     // An entity which manages the wakelocks.
71     private final WakelockController mWakelockController;
72     // A handler to process all the events on this thread in a synchronous manner
73     private final DisplayPowerProximityStateHandler mHandler;
74     // A runnable to execute the utility to update the power state.
75     private final Runnable mNudgeUpdatePowerState;
76     private Clock mClock;
77     // A listener which listen's to the events emitted by the proximity sensor.
78     private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
79         @Override
80         public void onSensorChanged(SensorEvent event) {
81             if (mProximitySensorEnabled) {
82                 final long time = mClock.uptimeMillis();
83                 final float distance = event.values[0];
84                 boolean positive = distance >= 0.0f && distance < mProximityThreshold;
85                 handleProximitySensorEvent(time, positive);
86             }
87         }
88 
89         @Override
90         public void onAccuracyChanged(Sensor sensor, int accuracy) {
91             // Not used.
92         }
93     };
94 
95     // The proximity sensor, or null if not available or needed.
96     private Sensor mProximitySensor;
97 
98     // The configurations for the associated display
99     private DisplayDeviceConfig mDisplayDeviceConfig;
100 
101     // True if a request has been made to wait for the proximity sensor to go negative.
102     @GuardedBy("mLock")
103     private boolean mPendingWaitForNegativeProximityLocked;
104 
105     // True if the device should wait for negative proximity sensor before
106     // waking up the screen.  This is set to false as soon as a negative
107     // proximity sensor measurement is observed or when the device is forced to
108     // go to sleep by the user.  While true, the screen remains off.
109     private boolean mWaitingForNegativeProximity;
110 
111     // True if the device should not take into account the proximity sensor
112     // until either the proximity sensor state changes, or there is no longer a
113     // request to listen to proximity sensor.
114     private boolean mIgnoreProximityUntilChanged;
115 
116     // Set to true if the proximity sensor listener has been registered
117     // with the sensor manager.
118     private boolean mProximitySensorEnabled;
119 
120     // The raw non-debounced proximity sensor state.
121     private int mPendingProximity = PROXIMITY_UNKNOWN;
122 
123     // -1 if fully debounced. Else, represents the time in ms when the debounce suspend blocker will
124     // be removed. Applies for both positive and negative proximity flips.
125     private long mPendingProximityDebounceTime = -1;
126 
127     // True if the screen was turned off because of the proximity sensor.
128     // When the screen turns on again, we report user activity to the power manager.
129     private boolean mScreenOffBecauseOfProximity;
130 
131     // The debounced proximity sensor state.
132     private int mProximity = PROXIMITY_UNKNOWN;
133 
134     // The actual proximity sensor threshold value.
135     private float mProximityThreshold;
136 
137     // A flag representing if the ramp is to be skipped when the proximity changes from positive
138     // to negative
139     private boolean mSkipRampBecauseOfProximityChangeToNegative = false;
140 
141     // The DisplayId of the associated Logical Display.
142     private int mDisplayId;
143 
144     /**
145      * Create a new instance of DisplayPowerProximityStateController.
146      *
147      * @param wakeLockController    WakelockController used to acquire/release wakelocks
148      * @param displayDeviceConfig   DisplayDeviceConfig instance from which the configs(Proximity
149      *                              Sensor) are to be loaded
150      * @param looper                A looper onto which the handler is to be associated.
151      * @param nudgeUpdatePowerState A runnable to execute the utility to update the power state
152      * @param displayId             The DisplayId of the associated Logical Display.
153      * @param sensorManager         The manager which lets us access the display's ProximitySensor
154      */
DisplayPowerProximityStateController( WakelockController wakeLockController, DisplayDeviceConfig displayDeviceConfig, Looper looper, Runnable nudgeUpdatePowerState, int displayId, SensorManager sensorManager, Injector injector)155     public DisplayPowerProximityStateController(
156             WakelockController wakeLockController, DisplayDeviceConfig displayDeviceConfig,
157             Looper looper,
158             Runnable nudgeUpdatePowerState, int displayId, SensorManager sensorManager,
159             Injector injector) {
160         if (injector == null) {
161             injector = new Injector();
162         }
163         mClock = injector.createClock();
164         mWakelockController = wakeLockController;
165         mHandler = new DisplayPowerProximityStateHandler(looper);
166         mNudgeUpdatePowerState = nudgeUpdatePowerState;
167         mDisplayDeviceConfig = displayDeviceConfig;
168         mDisplayId = displayId;
169         mTag = "DisplayPowerProximityStateController[" + mDisplayId + "]";
170         mSensorManager = sensorManager;
171         loadProximitySensor();
172     }
173 
174     /**
175      * Manages the pending state of the proximity.
176      */
updatePendingProximityRequestsLocked()177     public void updatePendingProximityRequestsLocked() {
178         synchronized (mLock) {
179             mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
180             mPendingWaitForNegativeProximityLocked = false;
181 
182             if (mIgnoreProximityUntilChanged) {
183                 // Also, lets stop waiting for negative proximity if we're ignoring it.
184                 mWaitingForNegativeProximity = false;
185             }
186         }
187     }
188 
189     /**
190      * Clean up all resources that are accessed via the {@link #mHandler} thread.
191      */
cleanup()192     public void cleanup() {
193         setProximitySensorEnabled(false);
194     }
195 
196     /**
197      * Returns true if the proximity sensor screen-off function is available.
198      */
isProximitySensorAvailable()199     public boolean isProximitySensorAvailable() {
200         return mProximitySensor != null;
201     }
202 
203     /**
204      * Sets the flag to indicate that the system is waiting for the negative proximity event
205      */
setPendingWaitForNegativeProximityLocked( boolean requestWaitForNegativeProximity)206     public boolean setPendingWaitForNegativeProximityLocked(
207             boolean requestWaitForNegativeProximity) {
208         synchronized (mLock) {
209             if (requestWaitForNegativeProximity
210                     && !mPendingWaitForNegativeProximityLocked) {
211                 mPendingWaitForNegativeProximityLocked = true;
212                 return true;
213             }
214             return false;
215         }
216     }
217 
218     /**
219      * Updates the proximity state of the display, based on the newly received DisplayPowerRequest
220      * and the target display state
221      */
updateProximityState( DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, int displayState)222     public void updateProximityState(
223             DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
224             int displayState) {
225         mSkipRampBecauseOfProximityChangeToNegative = false;
226         if (mProximitySensor != null) {
227             if (displayPowerRequest.useProximitySensor && displayState != Display.STATE_OFF) {
228                 // At this point the policy says that the screen should be on, but we've been
229                 // asked to listen to the prox sensor to adjust the display state, so lets make
230                 // sure the sensor is on.
231                 setProximitySensorEnabled(true);
232                 if (!mScreenOffBecauseOfProximity
233                         && mProximity == PROXIMITY_POSITIVE
234                         && !mIgnoreProximityUntilChanged) {
235                     // Prox sensor already reporting "near" so we should turn off the screen.
236                     // Also checked that we aren't currently set to ignore the proximity sensor
237                     // temporarily.
238                     mScreenOffBecauseOfProximity = true;
239                     sendOnProximityPositiveWithWakelock();
240                 }
241             } else if (mWaitingForNegativeProximity
242                     && mScreenOffBecauseOfProximity
243                     && mProximity == PROXIMITY_POSITIVE
244                     && displayState != Display.STATE_OFF) {
245                 // The policy says that we should have the screen on, but it's off due to the prox
246                 // and we've been asked to wait until the screen is far from the user to turn it
247                 // back on. Let keep the prox sensor on so we can tell when it's far again.
248                 setProximitySensorEnabled(true);
249             } else {
250                 // We haven't been asked to use the prox sensor and we're not waiting on the screen
251                 // to turn back on...so let's shut down the prox sensor.
252                 setProximitySensorEnabled(false);
253                 mWaitingForNegativeProximity = false;
254             }
255             if (mScreenOffBecauseOfProximity
256                     && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) {
257                 // The screen *was* off due to prox being near, but now it's "far" so lets turn
258                 // the screen back on.  Also turn it back on if we've been asked to ignore the
259                 // prox sensor temporarily.
260                 mScreenOffBecauseOfProximity = false;
261                 mSkipRampBecauseOfProximityChangeToNegative = true;
262                 sendOnProximityNegativeWithWakelock();
263             }
264         } else {
265             setProximitySensorEnabled(false);
266             mWaitingForNegativeProximity = false;
267             mIgnoreProximityUntilChanged = false;
268 
269             if (mScreenOffBecauseOfProximity) {
270                 // The screen *was* off due to prox being near, but now there's no prox sensor, so
271                 // let's turn the screen back on.
272                 mScreenOffBecauseOfProximity = false;
273                 mSkipRampBecauseOfProximityChangeToNegative = true;
274                 sendOnProximityNegativeWithWakelock();
275             }
276         }
277     }
278 
279     /**
280      * A utility to check if the brightness change ramp is to be skipped because the proximity was
281      * changed from positive to negative.
282      */
shouldSkipRampBecauseOfProximityChangeToNegative()283     public boolean shouldSkipRampBecauseOfProximityChangeToNegative() {
284         return mSkipRampBecauseOfProximityChangeToNegative;
285     }
286 
287     /**
288      * Represents of the screen is currently turned off because of the proximity state.
289      */
isScreenOffBecauseOfProximity()290     public boolean isScreenOffBecauseOfProximity() {
291         return mScreenOffBecauseOfProximity;
292     }
293 
294     /**
295      * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
296      * currently enabled and forcing the screen to be dark.
297      */
ignoreProximitySensorUntilChanged()298     public void ignoreProximitySensorUntilChanged() {
299         mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY);
300     }
301 
302     /**
303      * This adjusts the state of this class when a change in the DisplayDevice is detected.
304      */
notifyDisplayDeviceChanged(DisplayDeviceConfig displayDeviceConfig)305     public void notifyDisplayDeviceChanged(DisplayDeviceConfig displayDeviceConfig) {
306         this.mDisplayDeviceConfig = displayDeviceConfig;
307         loadProximitySensor();
308     }
309 
310     /**
311      * Used to dump the state.
312      *
313      * @param pw The PrintWriter used to dump the state.
314      */
dumpLocal(PrintWriter pw)315     public void dumpLocal(PrintWriter pw) {
316         pw.println();
317         pw.println("DisplayPowerProximityStateController:");
318         synchronized (mLock) {
319             pw.println("  mPendingWaitForNegativeProximityLocked="
320                     + mPendingWaitForNegativeProximityLocked);
321         }
322         pw.println("  mDisplayId=" + mDisplayId);
323         pw.println("  mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
324         pw.println("  mIgnoreProximityUntilChanged=" + mIgnoreProximityUntilChanged);
325         pw.println("  mProximitySensor=" + mProximitySensor);
326         pw.println("  mProximitySensorEnabled=" + mProximitySensorEnabled);
327         pw.println("  mProximityThreshold=" + mProximityThreshold);
328         pw.println("  mProximity=" + proximityToString(mProximity));
329         pw.println("  mPendingProximity=" + proximityToString(mPendingProximity));
330         pw.println("  mPendingProximityDebounceTime="
331                 + TimeUtils.formatUptime(mPendingProximityDebounceTime));
332         pw.println("  mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
333         pw.println("  mSkipRampBecauseOfProximityChangeToNegative="
334                 + mSkipRampBecauseOfProximityChangeToNegative);
335     }
336 
ignoreProximitySensorUntilChangedInternal()337     void ignoreProximitySensorUntilChangedInternal() {
338         if (!mIgnoreProximityUntilChanged
339                 && mProximity == PROXIMITY_POSITIVE) {
340             // Only ignore if it is still reporting positive (near)
341             mIgnoreProximityUntilChanged = true;
342             Slog.i(mTag, "Ignoring proximity");
343             mNudgeUpdatePowerState.run();
344         }
345     }
346 
sendOnProximityPositiveWithWakelock()347     private void sendOnProximityPositiveWithWakelock() {
348         mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_PROXIMITY_POSITIVE);
349         mHandler.post(mWakelockController.getOnProximityPositiveRunnable());
350     }
351 
sendOnProximityNegativeWithWakelock()352     private void sendOnProximityNegativeWithWakelock() {
353         mWakelockController.acquireWakelock(WakelockController.WAKE_LOCK_PROXIMITY_NEGATIVE);
354         mHandler.post(mWakelockController.getOnProximityNegativeRunnable());
355     }
356 
loadProximitySensor()357     private void loadProximitySensor() {
358         if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT || mDisplayId != Display.DEFAULT_DISPLAY) {
359             return;
360         }
361         mProximitySensor = SensorUtils.findSensor(mSensorManager,
362                 mDisplayDeviceConfig.getProximitySensor(), Sensor.TYPE_PROXIMITY);
363         if (mProximitySensor != null) {
364             mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
365                     TYPICAL_PROXIMITY_THRESHOLD);
366         }
367     }
368 
setProximitySensorEnabled(boolean enable)369     private void setProximitySensorEnabled(boolean enable) {
370         if (enable) {
371             if (!mProximitySensorEnabled) {
372                 // Register the listener.
373                 // Proximity sensor state already cleared initially.
374                 mProximitySensorEnabled = true;
375                 mIgnoreProximityUntilChanged = false;
376                 mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
377                         SensorManager.SENSOR_DELAY_NORMAL, mHandler);
378             }
379         } else {
380             if (mProximitySensorEnabled) {
381                 // Unregister the listener.
382                 // Clear the proximity sensor state for next time.
383                 mProximitySensorEnabled = false;
384                 mProximity = PROXIMITY_UNKNOWN;
385                 mIgnoreProximityUntilChanged = false;
386                 mPendingProximity = PROXIMITY_UNKNOWN;
387                 mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
388                 mSensorManager.unregisterListener(mProximitySensorListener);
389                 // release wake lock(must be last)
390                 boolean proxDebounceSuspendBlockerReleased =
391                         mWakelockController.releaseWakelock(
392                                 WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
393                 if (proxDebounceSuspendBlockerReleased) {
394                     mPendingProximityDebounceTime = -1;
395                 }
396             }
397         }
398     }
399 
handleProximitySensorEvent(long time, boolean positive)400     private void handleProximitySensorEvent(long time, boolean positive) {
401         if (mProximitySensorEnabled) {
402             if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
403                 return; // no change
404             }
405             if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
406                 return; // no change
407             }
408 
409             // Only accept a proximity sensor reading if it remains
410             // stable for the entire debounce delay.  We hold a wake lock while
411             // debouncing the sensor.
412             mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
413             if (positive) {
414                 mPendingProximity = PROXIMITY_POSITIVE;
415                 mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY;
416                 mWakelockController.acquireWakelock(
417                         WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); // acquire wake lock
418             } else {
419                 mPendingProximity = PROXIMITY_NEGATIVE;
420                 mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY;
421                 mWakelockController.acquireWakelock(
422                         WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); // acquire wake lock
423             }
424 
425             // Debounce the new sensor reading.
426             debounceProximitySensor();
427         }
428     }
429 
debounceProximitySensor()430     private void debounceProximitySensor() {
431         if (mProximitySensorEnabled
432                 && mPendingProximity != PROXIMITY_UNKNOWN
433                 && mPendingProximityDebounceTime >= 0) {
434             final long now = mClock.uptimeMillis();
435             if (mPendingProximityDebounceTime <= now) {
436                 if (mProximity != mPendingProximity) {
437                     // if the status of the sensor changed, stop ignoring.
438                     mIgnoreProximityUntilChanged = false;
439                     Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]");
440                 }
441                 // Sensor reading accepted.  Apply the change then release the wake lock.
442                 mProximity = mPendingProximity;
443                 mNudgeUpdatePowerState.run();
444                 // (must be last)
445                 boolean proxDebounceSuspendBlockerReleased =
446                         mWakelockController.releaseWakelock(
447                                 WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
448                 if (proxDebounceSuspendBlockerReleased) {
449                     mPendingProximityDebounceTime = -1;
450                 }
451 
452             } else {
453                 // Need to wait a little longer.
454                 // Debounce again later.  We continue holding a wake lock while waiting.
455                 Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
456                 mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
457             }
458         }
459     }
460 
461     private class DisplayPowerProximityStateHandler extends Handler {
DisplayPowerProximityStateHandler(Looper looper)462         DisplayPowerProximityStateHandler(Looper looper) {
463             super(looper, null, true /*async*/);
464         }
465 
466         @Override
handleMessage(Message msg)467         public void handleMessage(Message msg) {
468             switch (msg.what) {
469                 case MSG_PROXIMITY_SENSOR_DEBOUNCED:
470                     debounceProximitySensor();
471                     break;
472 
473                 case MSG_IGNORE_PROXIMITY:
474                     ignoreProximitySensorUntilChangedInternal();
475                     break;
476             }
477         }
478     }
479 
proximityToString(int state)480     private String proximityToString(int state) {
481         switch (state) {
482             case PROXIMITY_UNKNOWN:
483                 return "Unknown";
484             case PROXIMITY_NEGATIVE:
485                 return "Negative";
486             case PROXIMITY_POSITIVE:
487                 return "Positive";
488             default:
489                 return Integer.toString(state);
490         }
491     }
492 
493     @VisibleForTesting
getPendingWaitForNegativeProximityLocked()494     boolean getPendingWaitForNegativeProximityLocked() {
495         synchronized (mLock) {
496             return mPendingWaitForNegativeProximityLocked;
497         }
498     }
499 
500     @VisibleForTesting
getWaitingForNegativeProximity()501     boolean getWaitingForNegativeProximity() {
502         return mWaitingForNegativeProximity;
503     }
504 
505     @VisibleForTesting
shouldIgnoreProximityUntilChanged()506     boolean shouldIgnoreProximityUntilChanged() {
507         return mIgnoreProximityUntilChanged;
508     }
509 
isProximitySensorEnabled()510     boolean isProximitySensorEnabled() {
511         return mProximitySensorEnabled;
512     }
513 
514     @VisibleForTesting
getHandler()515     Handler getHandler() {
516         return mHandler;
517     }
518 
519     @VisibleForTesting
getPendingProximity()520     int getPendingProximity() {
521         return mPendingProximity;
522     }
523 
524     @VisibleForTesting
getProximity()525     int getProximity() {
526         return mProximity;
527     }
528 
529 
530     @VisibleForTesting
getPendingProximityDebounceTime()531     long getPendingProximityDebounceTime() {
532         return mPendingProximityDebounceTime;
533     }
534 
535     @VisibleForTesting
getProximitySensorListener()536     SensorEventListener getProximitySensorListener() {
537         return mProximitySensorListener;
538     }
539 
540     /** Functional interface for providing time. */
541     @VisibleForTesting
542     interface Clock {
543         /**
544          * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
545          */
uptimeMillis()546         long uptimeMillis();
547     }
548 
549     @VisibleForTesting
550     static class Injector {
createClock()551         Clock createClock() {
552             return () -> SystemClock.uptimeMillis();
553         }
554     }
555 }
556