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