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
17package com.android.systemui.statusbar.phone;
18
19import android.content.Context;
20import android.os.Handler;
21import android.os.PowerManager;
22import android.os.SystemClock;
23import android.os.Trace;
24import android.util.Log;
25
26import com.android.keyguard.KeyguardConstants;
27import com.android.keyguard.KeyguardUpdateMonitor;
28import com.android.keyguard.KeyguardUpdateMonitorCallback;
29import com.android.keyguard.LatencyTracker;
30import com.android.systemui.Dependency;
31import com.android.systemui.keyguard.KeyguardViewMediator;
32
33/**
34 * Controller which coordinates all the fingerprint unlocking actions with the UI.
35 */
36public 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
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
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
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
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
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
245    public void onStartedGoingToSleep(int why) {
246        mPendingAuthenticatedUserId = -1;
247    }
248
249    @Override
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
266    public int getMode() {
267        return mMode;
268    }
269
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
296    public void onFingerprintAuthFailed() {
297        cleanup();
298    }
299
300    @Override
301    public void onFingerprintError(int msgId, String errString) {
302        cleanup();
303    }
304
305    private void cleanup() {
306        releaseFingerprintWakeLock();
307    }
308
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
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