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