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