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