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