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.shouldControlScreenOff() && !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 (shouldDelayTransitionEnteringDoze || shouldDelayTransitionForUDFPS) { 168 mWakeLock.setAcquired(true); 169 } 170 171 if (!messagePending) { 172 if (DEBUG) { 173 Log.d(TAG, "Display state changed to " + screenState + " delayed by " 174 + (shouldDelayTransitionEnteringDoze ? ENTER_DOZE_DELAY : 1)); 175 } 176 177 if (shouldDelayTransitionEnteringDoze) { 178 mHandler.postDelayed(mApplyPendingScreenState, ENTER_DOZE_DELAY); 179 } else if (shouldDelayTransitionForUDFPS) { 180 mDozeLog.traceDisplayStateDelayedByUdfps(mPendingScreenState); 181 mHandler.postDelayed(mApplyPendingScreenState, UDFPS_DISPLAY_STATE_DELAY); 182 } else { 183 mHandler.post(mApplyPendingScreenState); 184 } 185 } else if (DEBUG) { 186 Log.d(TAG, "Pending display state change to " + screenState); 187 } 188 } else if (turningOff) { 189 mDozeHost.prepareForGentleSleep(() -> applyScreenState(screenState)); 190 } else { 191 applyScreenState(screenState); 192 } 193 } 194 applyPendingScreenState()195 private void applyPendingScreenState() { 196 if (mUdfpsController != null && mUdfpsController.isFingerDown()) { 197 mDozeLog.traceDisplayStateDelayedByUdfps(mPendingScreenState); 198 mHandler.postDelayed(mApplyPendingScreenState, UDFPS_DISPLAY_STATE_DELAY); 199 return; 200 } 201 202 applyScreenState(mPendingScreenState); 203 mPendingScreenState = Display.STATE_UNKNOWN; 204 } 205 applyScreenState(int screenState)206 private void applyScreenState(int screenState) { 207 if (screenState != Display.STATE_UNKNOWN) { 208 if (DEBUG) Log.d(TAG, "setDozeScreenState(" + screenState + ")"); 209 mDozeService.setDozeScreenState(screenState); 210 if (screenState == Display.STATE_DOZE) { 211 // If we're entering doze, update the doze screen brightness. We might have been 212 // clamping it to the dim brightness during the screen off animation, and we should 213 // now change it to the brightness we actually want according to the sensor. 214 mDozeScreenBrightness.updateBrightnessAndReady(false /* force */); 215 } 216 mPendingScreenState = Display.STATE_UNKNOWN; 217 mWakeLock.setAcquired(false); 218 } 219 } 220 221 private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { 222 @Override 223 public void onAllAuthenticatorsRegistered() { 224 updateUdfpsController(); 225 } 226 227 @Override 228 public void onEnrollmentsChanged() { 229 updateUdfpsController(); 230 } 231 }; 232 } 233