1 /*
2  * Copyright (C) 2015 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.statusbar.phone;
18 
19 import android.content.Context;
20 import android.os.Handler;
21 import android.os.PowerManager;
22 import android.os.SystemClock;
23 import android.os.Trace;
24 import android.util.Log;
25 
26 import com.android.keyguard.KeyguardConstants;
27 import com.android.keyguard.KeyguardUpdateMonitor;
28 import com.android.keyguard.KeyguardUpdateMonitorCallback;
29 import com.android.keyguard.LatencyTracker;
30 import com.android.systemui.Dependency;
31 import com.android.systemui.keyguard.KeyguardViewMediator;
32 
33 /**
34  * Controller which coordinates all the fingerprint unlocking actions with the UI.
35  */
36 public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
37 
38     private static final String TAG = "FingerprintController";
39     private static final boolean DEBUG_FP_WAKELOCK = KeyguardConstants.DEBUG_FP_WAKELOCK;
40     private static final long FINGERPRINT_WAKELOCK_TIMEOUT_MS = 15 * 1000;
41     private static final String FINGERPRINT_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
42 
43     /**
44      * Mode in which we don't need to wake up the device when we get a fingerprint.
45      */
46     public static final int MODE_NONE = 0;
47 
48     /**
49      * Mode in which we wake up the device, and directly dismiss Keyguard. Active when we acquire
50      * a fingerprint while the screen is off and the device was sleeping.
51      */
52     public static final int MODE_WAKE_AND_UNLOCK = 1;
53 
54     /**
55      * Mode in which we wake the device up, and fade out the Keyguard contents because they were
56      * already visible while pulsing in doze mode.
57      */
58     public static final int MODE_WAKE_AND_UNLOCK_PULSING = 2;
59 
60     /**
61      * Mode in which we wake up the device, but play the normal dismiss animation. Active when we
62      * acquire a fingerprint pulsing in doze mode.
63      */
64     public static final int MODE_SHOW_BOUNCER = 3;
65 
66     /**
67      * Mode in which we only wake up the device, and keyguard was not showing when we acquired a
68      * fingerprint.
69      * */
70     public static final int MODE_ONLY_WAKE = 4;
71 
72     /**
73      * Mode in which fingerprint unlocks the device.
74      */
75     public static final int MODE_UNLOCK = 5;
76 
77     /**
78      * Mode in which fingerprint brings up the bouncer because fingerprint unlocking is currently
79      * not allowed.
80      */
81     public static final int MODE_DISMISS_BOUNCER = 6;
82 
83     /**
84      * How much faster we collapse the lockscreen when authenticating with fingerprint.
85      */
86     private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
87 
88     private PowerManager mPowerManager;
89     private Handler mHandler = new Handler();
90     private PowerManager.WakeLock mWakeLock;
91     private KeyguardUpdateMonitor mUpdateMonitor;
92     private int mMode;
93     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
94     private StatusBarWindowManager mStatusBarWindowManager;
95     private DozeScrimController mDozeScrimController;
96     private KeyguardViewMediator mKeyguardViewMediator;
97     private ScrimController mScrimController;
98     private StatusBar mStatusBar;
99     private final UnlockMethodCache mUnlockMethodCache;
100     private final Context mContext;
101     private int mPendingAuthenticatedUserId = -1;
102 
FingerprintUnlockController(Context context, DozeScrimController dozeScrimController, KeyguardViewMediator keyguardViewMediator, ScrimController scrimController, StatusBar statusBar, UnlockMethodCache unlockMethodCache)103     public FingerprintUnlockController(Context context,
104             DozeScrimController dozeScrimController,
105             KeyguardViewMediator keyguardViewMediator,
106             ScrimController scrimController,
107             StatusBar statusBar,
108             UnlockMethodCache unlockMethodCache) {
109         mContext = context;
110         mPowerManager = context.getSystemService(PowerManager.class);
111         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
112         mUpdateMonitor.registerCallback(this);
113         mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
114         mDozeScrimController = dozeScrimController;
115         mKeyguardViewMediator = keyguardViewMediator;
116         mScrimController = scrimController;
117         mStatusBar = statusBar;
118         mUnlockMethodCache = unlockMethodCache;
119     }
120 
setStatusBarKeyguardViewManager( StatusBarKeyguardViewManager statusBarKeyguardViewManager)121     public void setStatusBarKeyguardViewManager(
122             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
123         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
124     }
125 
126     private final Runnable mReleaseFingerprintWakeLockRunnable = new Runnable() {
127         @Override
128         public void run() {
129             if (DEBUG_FP_WAKELOCK) {
130                 Log.i(TAG, "fp wakelock: TIMEOUT!!");
131             }
132             releaseFingerprintWakeLock();
133         }
134     };
135 
releaseFingerprintWakeLock()136     private void releaseFingerprintWakeLock() {
137         if (mWakeLock != null) {
138             mHandler.removeCallbacks(mReleaseFingerprintWakeLockRunnable);
139             if (DEBUG_FP_WAKELOCK) {
140                 Log.i(TAG, "releasing fp wakelock");
141             }
142             mWakeLock.release();
143             mWakeLock = null;
144         }
145     }
146 
147     @Override
onFingerprintAcquired()148     public void onFingerprintAcquired() {
149         Trace.beginSection("FingerprintUnlockController#onFingerprintAcquired");
150         releaseFingerprintWakeLock();
151         if (!mUpdateMonitor.isDeviceInteractive()) {
152             if (LatencyTracker.isEnabled(mContext)) {
153                 LatencyTracker.getInstance(mContext).onActionStart(
154                         LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK);
155             }
156             mWakeLock = mPowerManager.newWakeLock(
157                     PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
158             Trace.beginSection("acquiring wake-and-unlock");
159             mWakeLock.acquire();
160             Trace.endSection();
161             if (DEBUG_FP_WAKELOCK) {
162                 Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
163             }
164             mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
165                     FINGERPRINT_WAKELOCK_TIMEOUT_MS);
166             if (mDozeScrimController.isPulsing()) {
167 
168                 // If we are waking the device up while we are pulsing the clock and the
169                 // notifications would light up first, creating an unpleasant animation.
170                 // Defer changing the screen brightness by forcing doze brightness on our window
171                 // until the clock and the notifications are faded out.
172                 mStatusBarWindowManager.setForceDozeBrightness(true);
173             }
174         }
175         Trace.endSection();
176     }
177 
178     @Override
onFingerprintAuthenticated(int userId)179     public void onFingerprintAuthenticated(int userId) {
180         Trace.beginSection("FingerprintUnlockController#onFingerprintAuthenticated");
181         if (mUpdateMonitor.isGoingToSleep()) {
182             mPendingAuthenticatedUserId = userId;
183             Trace.endSection();
184             return;
185         }
186         boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
187         mMode = calculateMode();
188         if (!wasDeviceInteractive) {
189             if (DEBUG_FP_WAKELOCK) {
190                 Log.i(TAG, "fp wakelock: Authenticated, waking up...");
191             }
192             mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:FINGERPRINT");
193         }
194         Trace.beginSection("release wake-and-unlock");
195         releaseFingerprintWakeLock();
196         Trace.endSection();
197         switch (mMode) {
198             case MODE_DISMISS_BOUNCER:
199                 Trace.beginSection("MODE_DISMISS");
200                 mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
201                         false /* strongAuth */);
202                 Trace.endSection();
203                 break;
204             case MODE_UNLOCK:
205             case MODE_SHOW_BOUNCER:
206                 Trace.beginSection("MODE_UNLOCK or MODE_SHOW_BOUNCER");
207                 if (!wasDeviceInteractive) {
208                     mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
209                 }
210                 mStatusBarKeyguardViewManager.animateCollapsePanels(
211                         FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
212                 Trace.endSection();
213                 break;
214             case MODE_WAKE_AND_UNLOCK_PULSING:
215             case MODE_WAKE_AND_UNLOCK:
216                 if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
217                     Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
218                     mStatusBar.updateMediaMetaData(false /* metaDataChanged */,
219                             true /* allowEnterAnimation */);
220                 } else {
221                     Trace.beginSection("MODE_WAKE_AND_UNLOCK");
222                     mDozeScrimController.abortDoze();
223                 }
224                 mStatusBarWindowManager.setStatusBarFocusable(false);
225                 mKeyguardViewMediator.onWakeAndUnlocking();
226                 mScrimController.setWakeAndUnlocking();
227                 mDozeScrimController.setWakeAndUnlocking();
228                 if (mStatusBar.getNavigationBarView() != null) {
229                     mStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
230                 }
231                 Trace.endSection();
232                 break;
233             case MODE_ONLY_WAKE:
234             case MODE_NONE:
235                 break;
236         }
237         if (mMode != MODE_WAKE_AND_UNLOCK_PULSING) {
238             mStatusBarWindowManager.setForceDozeBrightness(false);
239         }
240         mStatusBar.notifyFpAuthModeChanged();
241         Trace.endSection();
242     }
243 
244     @Override
onStartedGoingToSleep(int why)245     public void onStartedGoingToSleep(int why) {
246         mPendingAuthenticatedUserId = -1;
247     }
248 
249     @Override
onFinishedGoingToSleep(int why)250     public void onFinishedGoingToSleep(int why) {
251         Trace.beginSection("FingerprintUnlockController#onFinishedGoingToSleep");
252         if (mPendingAuthenticatedUserId != -1) {
253 
254             // Post this to make sure it's executed after the device is fully locked.
255             mHandler.post(new Runnable() {
256                 @Override
257                 public void run() {
258                     onFingerprintAuthenticated(mPendingAuthenticatedUserId);
259                 }
260             });
261         }
262         mPendingAuthenticatedUserId = -1;
263         Trace.endSection();
264     }
265 
getMode()266     public int getMode() {
267         return mMode;
268     }
269 
calculateMode()270     private int calculateMode() {
271         boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithFingerprintAllowed();
272         if (!mUpdateMonitor.isDeviceInteractive()) {
273             if (!mStatusBarKeyguardViewManager.isShowing()) {
274                 return MODE_ONLY_WAKE;
275             } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
276                 return MODE_WAKE_AND_UNLOCK_PULSING;
277             } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
278                 return MODE_WAKE_AND_UNLOCK;
279             } else {
280                 return MODE_SHOW_BOUNCER;
281             }
282         }
283         if (mStatusBarKeyguardViewManager.isShowing()) {
284             if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) {
285                 return MODE_DISMISS_BOUNCER;
286             } else if (unlockingAllowed) {
287                 return MODE_UNLOCK;
288             } else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
289                 return MODE_SHOW_BOUNCER;
290             }
291         }
292         return MODE_NONE;
293     }
294 
295     @Override
onFingerprintAuthFailed()296     public void onFingerprintAuthFailed() {
297         cleanup();
298     }
299 
300     @Override
onFingerprintError(int msgId, String errString)301     public void onFingerprintError(int msgId, String errString) {
302         cleanup();
303     }
304 
cleanup()305     private void cleanup() {
306         releaseFingerprintWakeLock();
307     }
308 
startKeyguardFadingAway()309     public void startKeyguardFadingAway() {
310 
311         // Disable brightness override when the ambient contents are fully invisible.
312         mHandler.postDelayed(new Runnable() {
313             @Override
314             public void run() {
315                 mStatusBarWindowManager.setForceDozeBrightness(false);
316             }
317         }, StatusBar.FADE_KEYGUARD_DURATION_PULSING);
318     }
319 
finishKeyguardFadingAway()320     public void finishKeyguardFadingAway() {
321         mMode = MODE_NONE;
322         mStatusBarWindowManager.setForceDozeBrightness(false);
323         if (mStatusBar.getNavigationBarView() != null) {
324             mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
325         }
326         mStatusBar.notifyFpAuthModeChanged();
327     }
328 }
329