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