• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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