• 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 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