1 /* 2 * Copyright (C) 2017 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.systemui.doze; 18 19 import static com.android.systemui.doze.DozeMachine.State.DOZE; 20 import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; 21 import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; 22 import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING; 23 import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE; 24 25 import android.os.Handler; 26 import android.util.Log; 27 import android.view.Display; 28 29 import androidx.annotation.Nullable; 30 31 import com.android.keyguard.KeyguardUpdateMonitor; 32 import com.android.systemui.biometrics.AuthController; 33 import com.android.systemui.biometrics.UdfpsController; 34 import com.android.systemui.dagger.qualifiers.Main; 35 import com.android.systemui.doze.dagger.DozeScope; 36 import com.android.systemui.doze.dagger.WrappedService; 37 import com.android.systemui.statusbar.phone.DozeParameters; 38 import com.android.systemui.util.wakelock.SettableWakeLock; 39 import com.android.systemui.util.wakelock.WakeLock; 40 41 import javax.inject.Inject; 42 import javax.inject.Provider; 43 44 /** 45 * Controls the screen when dozing. 46 */ 47 @DozeScope 48 public class DozeScreenState implements DozeMachine.Part { 49 50 private static final boolean DEBUG = DozeService.DEBUG; 51 private static final String TAG = "DozeScreenState"; 52 53 /** 54 * Delay entering low power mode when animating to make sure that we'll have 55 * time to move all elements into their final positions while still at 60 fps. 56 */ 57 private static final int ENTER_DOZE_DELAY = 4000; 58 /** 59 * Hide wallpaper earlier when entering low power mode. The gap between 60 * hiding the wallpaper and changing the display mode is necessary to hide 61 * the black frame that's inherent to hardware specs. 62 */ 63 public static final int ENTER_DOZE_HIDE_WALLPAPER_DELAY = 2500; 64 65 /** 66 * Add an extra delay to the transition to DOZE when udfps is current activated before 67 * the display state transitions from ON => DOZE. 68 */ 69 public static final int UDFPS_DISPLAY_STATE_DELAY = 1200; 70 71 private final DozeMachine.Service mDozeService; 72 private final Handler mHandler; 73 private final Runnable mApplyPendingScreenState = this::applyPendingScreenState; 74 private final DozeParameters mParameters; 75 private final DozeHost mDozeHost; 76 private final AuthController mAuthController; 77 private final Provider<UdfpsController> mUdfpsControllerProvider; 78 @Nullable private UdfpsController mUdfpsController; 79 private final DozeLog mDozeLog; 80 private final DozeScreenBrightness mDozeScreenBrightness; 81 82 private int mPendingScreenState = Display.STATE_UNKNOWN; 83 private SettableWakeLock mWakeLock; 84 85 @Inject DozeScreenState( @rappedService DozeMachine.Service service, @Main Handler handler, DozeHost host, DozeParameters parameters, WakeLock wakeLock, AuthController authController, Provider<UdfpsController> udfpsControllerProvider, DozeLog dozeLog, DozeScreenBrightness dozeScreenBrightness)86 public DozeScreenState( 87 @WrappedService DozeMachine.Service service, 88 @Main Handler handler, 89 DozeHost host, 90 DozeParameters parameters, 91 WakeLock wakeLock, 92 AuthController authController, 93 Provider<UdfpsController> udfpsControllerProvider, 94 DozeLog dozeLog, 95 DozeScreenBrightness dozeScreenBrightness) { 96 mDozeService = service; 97 mHandler = handler; 98 mParameters = parameters; 99 mDozeHost = host; 100 mWakeLock = new SettableWakeLock(wakeLock, TAG); 101 mAuthController = authController; 102 mUdfpsControllerProvider = udfpsControllerProvider; 103 mDozeLog = dozeLog; 104 mDozeScreenBrightness = dozeScreenBrightness; 105 106 updateUdfpsController(); 107 if (mUdfpsController == null) { 108 mAuthController.addCallback(mAuthControllerCallback); 109 } 110 } 111 updateUdfpsController()112 private void updateUdfpsController() { 113 if (mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) { 114 mUdfpsController = mUdfpsControllerProvider.get(); 115 } else { 116 mUdfpsController = null; 117 } 118 } 119 120 @Override destroy()121 public void destroy() { 122 mAuthController.removeCallback(mAuthControllerCallback); 123 } 124 125 @Override transitionTo(DozeMachine.State oldState, DozeMachine.State newState)126 public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { 127 int screenState = newState.screenState(mParameters); 128 mDozeHost.cancelGentleSleep(); 129 130 if (newState == DozeMachine.State.FINISH) { 131 // Make sure not to apply the screen state after DozeService was destroyed. 132 mPendingScreenState = Display.STATE_UNKNOWN; 133 mHandler.removeCallbacks(mApplyPendingScreenState); 134 135 applyScreenState(screenState); 136 mWakeLock.setAcquired(false); 137 return; 138 } 139 140 if (screenState == Display.STATE_UNKNOWN) { 141 // We'll keep it in the existing state 142 return; 143 } 144 145 final boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState); 146 final boolean pulseEnding = oldState == DOZE_PULSE_DONE && newState.isAlwaysOn(); 147 final boolean turningOn = (oldState == DOZE_AOD_PAUSED || oldState == DOZE) 148 && newState.isAlwaysOn(); 149 final boolean turningOff = (oldState.isAlwaysOn() && newState == DOZE) 150 || (oldState == DOZE_AOD_PAUSING && newState == DOZE_AOD_PAUSED); 151 final boolean justInitialized = oldState == DozeMachine.State.INITIALIZED; 152 if (messagePending || justInitialized || pulseEnding || turningOn) { 153 // During initialization, we hide the navigation bar. That is however only applied after 154 // a traversal; setting the screen state here is immediate however, so it can happen 155 // that the screen turns on again before the navigation bar is hidden. To work around 156 // that, wait for a traversal to happen before applying the initial screen state. 157 mPendingScreenState = screenState; 158 159 // Delay screen state transitions even longer while animations are running. 160 boolean shouldDelayTransitionEnteringDoze = newState == DOZE_AOD 161 && mParameters.shouldDelayDisplayDozeTransition() && !turningOn; 162 163 // Delay screen state transition longer if UDFPS is actively authenticating a fp 164 boolean shouldDelayTransitionForUDFPS = newState == DOZE_AOD 165 && mUdfpsController != null && mUdfpsController.isFingerDown(); 166 167 if (!messagePending) { 168 if (DEBUG) { 169 Log.d(TAG, "Display state changed to " + screenState + " delayed by " 170 + (shouldDelayTransitionEnteringDoze ? ENTER_DOZE_DELAY : 1)); 171 } 172 173 if (shouldDelayTransitionEnteringDoze) { 174 if (justInitialized) { 175 // If we are delaying transitioning to doze and the display was not 176 // turned on we set it to 'on' first to make sure that the animation 177 // is visible before eventually moving it to doze state. 178 // The display might be off at this point for example on foldable devices 179 // when we switch displays and go to doze at the same time. 180 applyScreenState(Display.STATE_ON); 181 182 // Restore pending screen state as it gets cleared by 'applyScreenState' 183 mPendingScreenState = screenState; 184 } 185 186 mHandler.postDelayed(mApplyPendingScreenState, ENTER_DOZE_DELAY); 187 } else if (shouldDelayTransitionForUDFPS) { 188 mDozeLog.traceDisplayStateDelayedByUdfps(mPendingScreenState); 189 mHandler.postDelayed(mApplyPendingScreenState, UDFPS_DISPLAY_STATE_DELAY); 190 } else { 191 mHandler.post(mApplyPendingScreenState); 192 } 193 } else if (DEBUG) { 194 Log.d(TAG, "Pending display state change to " + screenState); 195 } 196 197 if (shouldDelayTransitionEnteringDoze || shouldDelayTransitionForUDFPS) { 198 mWakeLock.setAcquired(true); 199 } 200 } else if (turningOff) { 201 mDozeHost.prepareForGentleSleep(() -> applyScreenState(screenState)); 202 } else { 203 applyScreenState(screenState); 204 } 205 } 206 applyPendingScreenState()207 private void applyPendingScreenState() { 208 if (mUdfpsController != null && mUdfpsController.isFingerDown()) { 209 mDozeLog.traceDisplayStateDelayedByUdfps(mPendingScreenState); 210 mHandler.postDelayed(mApplyPendingScreenState, UDFPS_DISPLAY_STATE_DELAY); 211 return; 212 } 213 214 applyScreenState(mPendingScreenState); 215 mPendingScreenState = Display.STATE_UNKNOWN; 216 } 217 applyScreenState(int screenState)218 private void applyScreenState(int screenState) { 219 if (screenState != Display.STATE_UNKNOWN) { 220 if (DEBUG) Log.d(TAG, "setDozeScreenState(" + screenState + ")"); 221 mDozeService.setDozeScreenState(screenState); 222 if (screenState == Display.STATE_DOZE) { 223 // If we're entering doze, update the doze screen brightness. We might have been 224 // clamping it to the dim brightness during the screen off animation, and we should 225 // now change it to the brightness we actually want according to the sensor. 226 mDozeScreenBrightness.updateBrightnessAndReady(false /* force */); 227 } 228 mPendingScreenState = Display.STATE_UNKNOWN; 229 mWakeLock.setAcquired(false); 230 } 231 } 232 233 private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { 234 @Override 235 public void onAllAuthenticatorsRegistered() { 236 updateUdfpsController(); 237 } 238 239 @Override 240 public void onEnrollmentsChanged() { 241 updateUdfpsController(); 242 } 243 }; 244 } 245