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.internal.util.LatencyTracker;
30 import com.android.systemui.Dependency;
31 import com.android.systemui.keyguard.KeyguardViewMediator;
32 import com.android.systemui.keyguard.ScreenLifecycle;
33 import com.android.systemui.keyguard.WakefulnessLifecycle;
34 
35 import java.io.PrintWriter;
36 
37 /**
38  * Controller which coordinates all the fingerprint unlocking actions with the UI.
39  */
40 public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
41 
42     private static final String TAG = "FingerprintController";
43     private static final boolean DEBUG_FP_WAKELOCK = KeyguardConstants.DEBUG_FP_WAKELOCK;
44     private static final long FINGERPRINT_WAKELOCK_TIMEOUT_MS = 15 * 1000;
45     private static final String FINGERPRINT_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
46 
47     /**
48      * Mode in which we don't need to wake up the device when we get a fingerprint.
49      */
50     public static final int MODE_NONE = 0;
51 
52     /**
53      * Mode in which we wake up the device, and directly dismiss Keyguard. Active when we acquire
54      * a fingerprint while the screen is off and the device was sleeping.
55      */
56     public static final int MODE_WAKE_AND_UNLOCK = 1;
57 
58     /**
59      * Mode in which we wake the device up, and fade out the Keyguard contents because they were
60      * already visible while pulsing in doze mode.
61      */
62     public static final int MODE_WAKE_AND_UNLOCK_PULSING = 2;
63 
64     /**
65      * Mode in which we wake up the device, but play the normal dismiss animation. Active when we
66      * acquire a fingerprint pulsing in doze mode.
67      */
68     public static final int MODE_SHOW_BOUNCER = 3;
69 
70     /**
71      * Mode in which we only wake up the device, and keyguard was not showing when we acquired a
72      * fingerprint.
73      * */
74     public static final int MODE_ONLY_WAKE = 4;
75 
76     /**
77      * Mode in which fingerprint unlocks the device.
78      */
79     public static final int MODE_UNLOCK = 5;
80 
81     /**
82      * Mode in which fingerprint brings up the bouncer because fingerprint unlocking is currently
83      * not allowed.
84      */
85     public static final int MODE_DISMISS_BOUNCER = 6;
86 
87     /**
88      * Mode in which fingerprint wakes and unlocks the device from a dream.
89      */
90     public static final int MODE_WAKE_AND_UNLOCK_FROM_DREAM = 7;
91 
92     /**
93      * How much faster we collapse the lockscreen when authenticating with fingerprint.
94      */
95     private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
96 
97     private PowerManager mPowerManager;
98     private Handler mHandler = new Handler();
99     private PowerManager.WakeLock mWakeLock;
100     private KeyguardUpdateMonitor mUpdateMonitor;
101     private int mMode;
102     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
103     private StatusBarWindowManager mStatusBarWindowManager;
104     private DozeScrimController mDozeScrimController;
105     private KeyguardViewMediator mKeyguardViewMediator;
106     private ScrimController mScrimController;
107     private StatusBar mStatusBar;
108     private final UnlockMethodCache mUnlockMethodCache;
109     private final Context mContext;
110     private int mPendingAuthenticatedUserId = -1;
111     private boolean mPendingShowBouncer;
112     private boolean mHasScreenTurnedOnSinceAuthenticating;
113 
FingerprintUnlockController(Context context, DozeScrimController dozeScrimController, KeyguardViewMediator keyguardViewMediator, ScrimController scrimController, StatusBar statusBar, UnlockMethodCache unlockMethodCache)114     public FingerprintUnlockController(Context context,
115             DozeScrimController dozeScrimController,
116             KeyguardViewMediator keyguardViewMediator,
117             ScrimController scrimController,
118             StatusBar statusBar,
119             UnlockMethodCache unlockMethodCache) {
120         mContext = context;
121         mPowerManager = context.getSystemService(PowerManager.class);
122         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
123         mUpdateMonitor.registerCallback(this);
124         Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver);
125         Dependency.get(ScreenLifecycle.class).addObserver(mScreenObserver);
126         mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
127         mDozeScrimController = dozeScrimController;
128         mKeyguardViewMediator = keyguardViewMediator;
129         mScrimController = scrimController;
130         mStatusBar = statusBar;
131         mUnlockMethodCache = unlockMethodCache;
132     }
133 
setStatusBarKeyguardViewManager( StatusBarKeyguardViewManager statusBarKeyguardViewManager)134     public void setStatusBarKeyguardViewManager(
135             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
136         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
137     }
138 
139     private final Runnable mReleaseFingerprintWakeLockRunnable = new Runnable() {
140         @Override
141         public void run() {
142             if (DEBUG_FP_WAKELOCK) {
143                 Log.i(TAG, "fp wakelock: TIMEOUT!!");
144             }
145             releaseFingerprintWakeLock();
146         }
147     };
148 
releaseFingerprintWakeLock()149     private void releaseFingerprintWakeLock() {
150         if (mWakeLock != null) {
151             mHandler.removeCallbacks(mReleaseFingerprintWakeLockRunnable);
152             if (DEBUG_FP_WAKELOCK) {
153                 Log.i(TAG, "releasing fp wakelock");
154             }
155             mWakeLock.release();
156             mWakeLock = null;
157         }
158     }
159 
160     @Override
onFingerprintAcquired()161     public void onFingerprintAcquired() {
162         Trace.beginSection("FingerprintUnlockController#onFingerprintAcquired");
163         releaseFingerprintWakeLock();
164         if (!mUpdateMonitor.isDeviceInteractive()) {
165             if (LatencyTracker.isEnabled(mContext)) {
166                 LatencyTracker.getInstance(mContext).onActionStart(
167                         LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK);
168             }
169             mWakeLock = mPowerManager.newWakeLock(
170                     PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
171             Trace.beginSection("acquiring wake-and-unlock");
172             mWakeLock.acquire();
173             Trace.endSection();
174             if (DEBUG_FP_WAKELOCK) {
175                 Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
176             }
177             mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
178                     FINGERPRINT_WAKELOCK_TIMEOUT_MS);
179         }
180         Trace.endSection();
181     }
182 
pulsingOrAod()183     private boolean pulsingOrAod() {
184         final ScrimState scrimState = mScrimController.getState();
185         return scrimState == ScrimState.AOD
186                 || scrimState == ScrimState.PULSING;
187     }
188 
189     @Override
onFingerprintAuthenticated(int userId)190     public void onFingerprintAuthenticated(int userId) {
191         Trace.beginSection("FingerprintUnlockController#onFingerprintAuthenticated");
192         if (mUpdateMonitor.isGoingToSleep()) {
193             mPendingAuthenticatedUserId = userId;
194             Trace.endSection();
195             return;
196         }
197         startWakeAndUnlock(calculateMode());
198     }
199 
startWakeAndUnlock(int mode)200     public void startWakeAndUnlock(int mode) {
201         // TODO(b/62444020): remove when this bug is fixed
202         Log.v(TAG, "startWakeAndUnlock(" + mode + ")");
203         boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
204         mMode = mode;
205         mHasScreenTurnedOnSinceAuthenticating = false;
206         if (mMode == MODE_WAKE_AND_UNLOCK_PULSING && pulsingOrAod()) {
207             // If we are waking the device up while we are pulsing the clock and the
208             // notifications would light up first, creating an unpleasant animation.
209             // Defer changing the screen brightness by forcing doze brightness on our window
210             // until the clock and the notifications are faded out.
211             mStatusBarWindowManager.setForceDozeBrightness(true);
212         }
213         if (!wasDeviceInteractive) {
214             if (DEBUG_FP_WAKELOCK) {
215                 Log.i(TAG, "fp wakelock: Authenticated, waking up...");
216             }
217             mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:FINGERPRINT");
218         }
219         Trace.beginSection("release wake-and-unlock");
220         releaseFingerprintWakeLock();
221         Trace.endSection();
222         switch (mMode) {
223             case MODE_DISMISS_BOUNCER:
224                 Trace.beginSection("MODE_DISMISS");
225                 mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
226                         false /* strongAuth */);
227                 Trace.endSection();
228                 break;
229             case MODE_UNLOCK:
230             case MODE_SHOW_BOUNCER:
231                 Trace.beginSection("MODE_UNLOCK or MODE_SHOW_BOUNCER");
232                 if (!wasDeviceInteractive) {
233                     mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
234                     mPendingShowBouncer = true;
235                 } else {
236                     showBouncer();
237                 }
238                 Trace.endSection();
239                 break;
240             case MODE_WAKE_AND_UNLOCK_FROM_DREAM:
241             case MODE_WAKE_AND_UNLOCK_PULSING:
242             case MODE_WAKE_AND_UNLOCK:
243                 if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
244                     Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
245                     mStatusBar.updateMediaMetaData(false /* metaDataChanged */,
246                             true /* allowEnterAnimation */);
247                 } else if (mMode == MODE_WAKE_AND_UNLOCK){
248                     Trace.beginSection("MODE_WAKE_AND_UNLOCK");
249                 } else {
250                     Trace.beginSection("MODE_WAKE_AND_UNLOCK_FROM_DREAM");
251                     mUpdateMonitor.awakenFromDream();
252                 }
253                 mStatusBarWindowManager.setStatusBarFocusable(false);
254                 mKeyguardViewMediator.onWakeAndUnlocking();
255                 if (mStatusBar.getNavigationBarView() != null) {
256                     mStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
257                 }
258                 Trace.endSection();
259                 break;
260             case MODE_ONLY_WAKE:
261             case MODE_NONE:
262                 break;
263         }
264         mStatusBar.notifyFpAuthModeChanged();
265         Trace.endSection();
266     }
267 
showBouncer()268     private void showBouncer() {
269         if (calculateMode() == MODE_SHOW_BOUNCER) {
270             mStatusBarKeyguardViewManager.showBouncer(false);
271         }
272         mStatusBarKeyguardViewManager.animateCollapsePanels(
273                 FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
274         mPendingShowBouncer = false;
275     }
276 
277     @Override
onStartedGoingToSleep(int why)278     public void onStartedGoingToSleep(int why) {
279         resetMode();
280         mPendingAuthenticatedUserId = -1;
281     }
282 
283     @Override
onFinishedGoingToSleep(int why)284     public void onFinishedGoingToSleep(int why) {
285         Trace.beginSection("FingerprintUnlockController#onFinishedGoingToSleep");
286         if (mPendingAuthenticatedUserId != -1) {
287 
288             // Post this to make sure it's executed after the device is fully locked.
289             mHandler.post(new Runnable() {
290                 @Override
291                 public void run() {
292                     onFingerprintAuthenticated(mPendingAuthenticatedUserId);
293                 }
294             });
295         }
296         mPendingAuthenticatedUserId = -1;
297         Trace.endSection();
298     }
299 
hasPendingAuthentication()300     public boolean hasPendingAuthentication() {
301         return mPendingAuthenticatedUserId != -1
302                 && mUpdateMonitor.isUnlockingWithFingerprintAllowed()
303                 && mPendingAuthenticatedUserId == KeyguardUpdateMonitor.getCurrentUser();
304     }
305 
getMode()306     public int getMode() {
307         return mMode;
308     }
309 
calculateMode()310     private int calculateMode() {
311         boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithFingerprintAllowed();
312         boolean deviceDreaming = mUpdateMonitor.isDreaming();
313 
314         if (!mUpdateMonitor.isDeviceInteractive()) {
315             if (!mStatusBarKeyguardViewManager.isShowing()) {
316                 return MODE_ONLY_WAKE;
317             } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
318                 return MODE_WAKE_AND_UNLOCK_PULSING;
319             } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
320                 return MODE_WAKE_AND_UNLOCK;
321             } else {
322                 return MODE_SHOW_BOUNCER;
323             }
324         }
325         if (unlockingAllowed && deviceDreaming) {
326             return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
327         }
328         if (mStatusBarKeyguardViewManager.isShowing()) {
329             if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) {
330                 return MODE_DISMISS_BOUNCER;
331             } else if (unlockingAllowed) {
332                 return MODE_UNLOCK;
333             } else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
334                 return MODE_SHOW_BOUNCER;
335             }
336         }
337         return MODE_NONE;
338     }
339 
340     @Override
onFingerprintAuthFailed()341     public void onFingerprintAuthFailed() {
342         cleanup();
343     }
344 
345     @Override
onFingerprintError(int msgId, String errString)346     public void onFingerprintError(int msgId, String errString) {
347         cleanup();
348     }
349 
cleanup()350     private void cleanup() {
351         releaseFingerprintWakeLock();
352     }
353 
startKeyguardFadingAway()354     public void startKeyguardFadingAway() {
355 
356         // Disable brightness override when the ambient contents are fully invisible.
357         mHandler.postDelayed(new Runnable() {
358             @Override
359             public void run() {
360                 mStatusBarWindowManager.setForceDozeBrightness(false);
361             }
362         }, StatusBar.FADE_KEYGUARD_DURATION_PULSING);
363     }
364 
finishKeyguardFadingAway()365     public void finishKeyguardFadingAway() {
366         resetMode();
367     }
368 
resetMode()369     private void resetMode() {
370         mMode = MODE_NONE;
371         mStatusBarWindowManager.setForceDozeBrightness(false);
372         if (mStatusBar.getNavigationBarView() != null) {
373             mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
374         }
375         mStatusBar.notifyFpAuthModeChanged();
376     }
377 
378     private final WakefulnessLifecycle.Observer mWakefulnessObserver =
379             new WakefulnessLifecycle.Observer() {
380         @Override
381         public void onFinishedWakingUp() {
382             if (mPendingShowBouncer) {
383                 FingerprintUnlockController.this.showBouncer();
384             }
385         }
386     };
387 
388     private final ScreenLifecycle.Observer mScreenObserver =
389             new ScreenLifecycle.Observer() {
390                 @Override
391                 public void onScreenTurnedOn() {
392                     mHasScreenTurnedOnSinceAuthenticating = true;
393                 }
394             };
395 
hasScreenTurnedOnSinceAuthenticating()396     public boolean hasScreenTurnedOnSinceAuthenticating() {
397         return mHasScreenTurnedOnSinceAuthenticating;
398     }
399 
dump(PrintWriter pw)400     public void dump(PrintWriter pw) {
401         pw.println(" FingerprintUnlockController:");
402         pw.print("   mMode="); pw.println(mMode);
403         pw.print("   mWakeLock="); pw.println(mWakeLock);
404     }
405 
406     /**
407      * Successful authentication with fingerprint that wakes up the device.
408      */
isWakeAndUnlock()409     public boolean isWakeAndUnlock() {
410         return mMode == MODE_WAKE_AND_UNLOCK
411                 || mMode == MODE_WAKE_AND_UNLOCK_PULSING
412                 || mMode == MODE_WAKE_AND_UNLOCK_FROM_DREAM;
413     }
414 
415     /**
416      * Successful authentication with fingerprint when the screen was either on or off.
417      */
isFingerprintUnlock()418     public boolean isFingerprintUnlock() {
419         return isWakeAndUnlock() || mMode == MODE_UNLOCK;
420     }
421 }
422