FaceUnlock.java revision 000464ac012471d301c6e48a8228291519915e17
1/* 2 * Copyright (C) 2012 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.internal.policy.impl.keyguard; 18 19import com.android.internal.policy.IFaceLockCallback; 20import com.android.internal.policy.IFaceLockInterface; 21import com.android.internal.widget.LockPatternUtils; 22 23import android.app.admin.DevicePolicyManager; 24import android.content.ComponentName; 25import android.content.Context; 26import android.content.Intent; 27import android.content.ServiceConnection; 28import android.os.Handler; 29import android.os.IBinder; 30import android.os.Looper; 31import android.os.Message; 32import android.os.RemoteException; 33import android.util.Log; 34import android.view.View; 35 36public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { 37 38 private static final boolean DEBUG = false; 39 private static final String TAG = "FULLockscreen"; 40 41 private final Context mContext; 42 private final LockPatternUtils mLockPatternUtils; 43 44 // TODO: is mServiceRunning needed or can we just use mIsRunning or check if mService is null? 45 private boolean mServiceRunning = false; 46 // TODO: now that the code has been restructure to do almost all operations from a handler, this 47 // lock may no longer be necessary. 48 private final Object mServiceRunningLock = new Object(); 49 private IFaceLockInterface mService; 50 private boolean mBoundToService = false; 51 private View mFaceUnlockView; 52 53 private Handler mHandler; 54 private final int MSG_SHOW_FACE_UNLOCK_VIEW = 0; 55 private final int MSG_HIDE_FACE_UNLOCK_VIEW = 1; 56 private final int MSG_SERVICE_CONNECTED = 2; 57 private final int MSG_SERVICE_DISCONNECTED = 3; 58 private final int MSG_UNLOCK = 4; 59 private final int MSG_CANCEL = 5; 60 private final int MSG_REPORT_FAILED_ATTEMPT = 6; 61 private final int MSG_EXPOSE_FALLBACK = 7; 62 private final int MSG_POKE_WAKELOCK = 8; 63 64 // TODO: This was added for the purpose of adhering to what the biometric interface expects 65 // the isRunning() function to return. However, it is probably not necessary to have both 66 // mRunning and mServiceRunning. I'd just rather wait to change that logic. 67 private volatile boolean mIsRunning = false; 68 69 // Long enough to stay visible while the service starts 70 // Short enough to not have to wait long for backup if service fails to start or crashes 71 // The service can take a couple of seconds to start on the first try after boot 72 private final int SERVICE_STARTUP_VIEW_TIMEOUT = 3000; 73 74 // So the user has a consistent amount of time when brought to the backup method from Face 75 // Unlock 76 private final int BACKUP_LOCK_TIMEOUT = 5000; 77 78 KeyguardSecurityCallback mKeyguardScreenCallback; 79 80 /** 81 * Stores some of the structures that Face Unlock will need to access and creates the handler 82 * will be used to execute messages on the UI thread. 83 */ 84 public FaceUnlock(Context context) { 85 mContext = context; 86 mLockPatternUtils = new LockPatternUtils(context); 87 mHandler = new Handler(this); 88 } 89 90 public void setKeyguardCallback(KeyguardSecurityCallback keyguardScreenCallback) { 91 mKeyguardScreenCallback = keyguardScreenCallback; 92 } 93 94 /** 95 * Stores and displays the view that Face Unlock is allowed to draw within. 96 * TODO: since the layout object will eventually be shared by multiple biometric unlock 97 * methods, we will have to add our other views (background, cancel button) here. 98 */ 99 public void initializeView(View biometricUnlockView) { 100 Log.d(TAG, "initializeView()"); 101 mFaceUnlockView = biometricUnlockView; 102 } 103 104 /** 105 * Indicates whether Face Unlock is currently running. 106 */ 107 public boolean isRunning() { 108 return mIsRunning; 109 } 110 111 /** 112 * Sets the Face Unlock view to visible, hiding it after the specified amount of time. If 113 * timeoutMillis is 0, no hide is performed. Called on the UI thread. 114 */ 115 public void show(long timeoutMillis) { 116 if (DEBUG) Log.d(TAG, "show()"); 117 if (mHandler.getLooper() != Looper.myLooper()) { 118 Log.e(TAG, "show() called off of the UI thread"); 119 } 120 removeDisplayMessages(); 121 if (timeoutMillis > 0) { 122 mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACE_UNLOCK_VIEW, timeoutMillis); 123 } 124 } 125 126 /** 127 * Hides the Face Unlock view. 128 */ 129 public void hide() { 130 if (DEBUG) Log.d(TAG, "hide()"); 131 // Remove messages to prevent a delayed show message from undo-ing the hide 132 removeDisplayMessages(); 133 mHandler.sendEmptyMessage(MSG_HIDE_FACE_UNLOCK_VIEW); 134 } 135 136 /** 137 * Binds to the Face Unlock service. Face Unlock will be started when the bind completes. The 138 * Face Unlock view is displayed to hide the backup lock while the service is starting up. 139 * Called on the UI thread. 140 */ 141 public boolean start() { 142 if (DEBUG) Log.d(TAG, "start()"); 143 if (mHandler.getLooper() != Looper.myLooper()) { 144 Log.e(TAG, "start() called off of the UI thread"); 145 } 146 147 if (mIsRunning) { 148 Log.w(TAG, "start() called when already running"); 149 } 150 151 // Show Face Unlock view, but only for a little bit so lockpattern will become visible if 152 // Face Unlock fails to start or crashes 153 // This must show before bind to guarantee that Face Unlock has a place to display 154 show(SERVICE_STARTUP_VIEW_TIMEOUT); 155 if (!mBoundToService) { 156 Log.d(TAG, "Binding to Face Unlock service"); 157 mContext.bindService(new Intent(IFaceLockInterface.class.getName()), 158 mConnection, 159 Context.BIND_AUTO_CREATE, 160 mLockPatternUtils.getCurrentUser()); 161 mBoundToService = true; 162 } else { 163 Log.w(TAG, "Attempt to bind to Face Unlock when already bound"); 164 } 165 166 mIsRunning = true; 167 return true; 168 } 169 170 /** 171 * Stops Face Unlock and unbinds from the service. Called on the UI thread. 172 */ 173 public boolean stop() { 174 if (DEBUG) Log.d(TAG, "stop()"); 175 if (mHandler.getLooper() != Looper.myLooper()) { 176 Log.e(TAG, "stop() called off of the UI thread"); 177 } 178 179 boolean mWasRunning = mIsRunning; 180 stopUi(); 181 182 if (mBoundToService) { 183 if (mService != null) { 184 try { 185 mService.unregisterCallback(mFaceUnlockCallback); 186 } catch (RemoteException e) { 187 // Not much we can do 188 } 189 } 190 Log.d(TAG, "Unbinding from Face Unlock service"); 191 mContext.unbindService(mConnection); 192 mBoundToService = false; 193 } else { 194 // This is usually not an error when this happens. Sometimes we will tell it to 195 // unbind multiple times because it's called from both onWindowFocusChanged and 196 // onDetachedFromWindow. 197 if (DEBUG) Log.d(TAG, "Attempt to unbind from Face Unlock when not bound"); 198 } 199 mIsRunning = false; 200 return mWasRunning; 201 } 202 203 /** 204 * Frees up resources used by Face Unlock and stops it if it is still running. 205 */ 206 public void cleanUp() { 207 if (DEBUG) Log.d(TAG, "cleanUp()"); 208 if (mService != null) { 209 try { 210 mService.unregisterCallback(mFaceUnlockCallback); 211 } catch (RemoteException e) { 212 // Not much we can do 213 } 214 stopUi(); 215 mService = null; 216 } 217 } 218 219 /** 220 * Returns the Device Policy Manager quality for Face Unlock, which is BIOMETRIC_WEAK. 221 */ 222 public int getQuality() { 223 return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; 224 } 225 226 /** 227 * Handles messages such that everything happens on the UI thread in a deterministic order. 228 * Calls from the Face Unlock service come from binder threads. Calls from lockscreen typically 229 * come from the UI thread. This makes sure there are no race conditions between those calls. 230 */ 231 public boolean handleMessage(Message msg) { 232 switch (msg.what) { 233 case MSG_SHOW_FACE_UNLOCK_VIEW: 234 handleShowFaceUnlockView(); 235 break; 236 case MSG_HIDE_FACE_UNLOCK_VIEW: 237 handleHideFaceUnlockView(); 238 break; 239 case MSG_SERVICE_CONNECTED: 240 handleServiceConnected(); 241 break; 242 case MSG_SERVICE_DISCONNECTED: 243 handleServiceDisconnected(); 244 break; 245 case MSG_UNLOCK: 246 handleUnlock(); 247 break; 248 case MSG_CANCEL: 249 handleCancel(); 250 break; 251 case MSG_REPORT_FAILED_ATTEMPT: 252 handleReportFailedAttempt(); 253 break; 254 case MSG_EXPOSE_FALLBACK: 255 handleExposeFallback(); 256 break; 257 case MSG_POKE_WAKELOCK: 258 handlePokeWakelock(msg.arg1); 259 break; 260 default: 261 Log.e(TAG, "Unhandled message"); 262 return false; 263 } 264 return true; 265 } 266 267 /** 268 * Sets the Face Unlock view to visible, thus covering the backup lock. 269 */ 270 void handleShowFaceUnlockView() { 271 if (DEBUG) Log.d(TAG, "handleShowFaceUnlockView()"); 272 // Not required 273 } 274 275 /** 276 * Hide face unlock and show backup 277 */ 278 void handleHideFaceUnlockView() { 279 if (DEBUG) Log.d(TAG, "handleHideFaceUnlockView()"); 280 mKeyguardScreenCallback.showBackupSecurity(); 281 } 282 283 /** 284 * Tells the service to start its UI via an AIDL interface. Called when the 285 * onServiceConnected() callback is received. 286 */ 287 void handleServiceConnected() { 288 Log.d(TAG, "handleServiceConnected()"); 289 290 // It is possible that an unbind has occurred in the time between the bind and when this 291 // function is reached. If an unbind has already occurred, proceeding on to call startUi() 292 // can result in a fatal error. Note that the onServiceConnected() callback is 293 // asynchronous, so this possibility would still exist if we executed this directly in 294 // onServiceConnected() rather than using a handler. 295 if (!mBoundToService) { 296 Log.d(TAG, "Dropping startUi() in handleServiceConnected() because no longer bound"); 297 return; 298 } 299 300 try { 301 mService.registerCallback(mFaceUnlockCallback); 302 } catch (RemoteException e) { 303 Log.e(TAG, "Caught exception connecting to Face Unlock: " + e.toString()); 304 mService = null; 305 mBoundToService = false; 306 mIsRunning = false; 307 return; 308 } 309 310 if (mFaceUnlockView != null) { 311 IBinder windowToken = mFaceUnlockView.getWindowToken(); 312 if (windowToken != null) { 313 // When switching between portrait and landscape view while Face Unlock is running, 314 // the screen will eventually go dark unless we poke the wakelock when Face Unlock 315 // is restarted. 316 mKeyguardScreenCallback.userActivity(0); 317 318 int[] position; 319 position = new int[2]; 320 mFaceUnlockView.getLocationInWindow(position); 321 startUi(windowToken, position[0], position[1], mFaceUnlockView.getWidth(), 322 mFaceUnlockView.getHeight()); 323 } else { 324 Log.e(TAG, "windowToken is null in handleServiceConnected()"); 325 } 326 } 327 } 328 329 /** 330 * Called when the onServiceDisconnected() callback is received. This should not happen during 331 * normal operation. It indicates an error has occurred. 332 */ 333 void handleServiceDisconnected() { 334 Log.e(TAG, "handleServiceDisconnected()"); 335 // TODO: this lock may no longer be needed now that everything is being called from a 336 // handler 337 synchronized (mServiceRunningLock) { 338 mService = null; 339 mServiceRunning = false; 340 } 341 mBoundToService = false; 342 mIsRunning = false; 343 } 344 345 /** 346 * Stops the Face Unlock service and tells the device to grant access to the user. Shows the 347 * Face Unlock view to keep the backup lock covered while the device unlocks. 348 */ 349 void handleUnlock() { 350 if (DEBUG) Log.d(TAG, "handleUnlock()"); 351 removeDisplayMessages(); 352 stop(); 353 mKeyguardScreenCallback.reportSuccessfulUnlockAttempt(); 354 mKeyguardScreenCallback.dismiss(true); 355 } 356 357 /** 358 * Stops the Face Unlock service and exposes the backup lock. 359 */ 360 void handleCancel() { 361 if (DEBUG) Log.d(TAG, "handleCancel()"); 362 mKeyguardScreenCallback.dismiss(false); 363 stop(); 364 mKeyguardScreenCallback.userActivity(BACKUP_LOCK_TIMEOUT); 365 } 366 367 /** 368 * Increments the number of failed Face Unlock attempts. 369 */ 370 void handleReportFailedAttempt() { 371 if (DEBUG) Log.d(TAG, "handleReportFailedAttempt()"); 372 mKeyguardScreenCallback.reportFailedUnlockAttempt(); 373 } 374 375 /** 376 * Hides the Face Unlock view to expose the backup lock. Called when the Face Unlock service UI 377 * is started, indicating there is no need to continue displaying the underlying view because 378 * the service UI is now covering the backup lock. 379 */ 380 void handleExposeFallback() { 381 if (DEBUG) Log.d(TAG, "handleExposeFallback()"); 382 // No longer required because face unlock doesn't cover backup unlock. 383 } 384 385 /** 386 * Pokes the wakelock to keep the screen alive and active for a specific amount of time. 387 */ 388 void handlePokeWakelock(int millis) { 389 mKeyguardScreenCallback.userActivity(millis); 390 } 391 392 /** 393 * Removes show and hide messages from the message queue. Called to prevent delayed show/hide 394 * messages from undoing a new message. 395 */ 396 private void removeDisplayMessages() { 397 mHandler.removeMessages(MSG_SHOW_FACE_UNLOCK_VIEW); 398 mHandler.removeMessages(MSG_HIDE_FACE_UNLOCK_VIEW); 399 } 400 401 /** 402 * Implements service connection methods. 403 */ 404 private ServiceConnection mConnection = new ServiceConnection() { 405 /** 406 * Called when the Face Unlock service connects after calling bind(). 407 */ 408 public void onServiceConnected(ComponentName className, IBinder iservice) { 409 Log.d(TAG, "Connected to Face Unlock service"); 410 mService = IFaceLockInterface.Stub.asInterface(iservice); 411 mHandler.sendEmptyMessage(MSG_SERVICE_CONNECTED); 412 } 413 414 /** 415 * Called if the Face Unlock service unexpectedly disconnects. This indicates an error. 416 */ 417 public void onServiceDisconnected(ComponentName className) { 418 Log.e(TAG, "Unexpected disconnect from Face Unlock service"); 419 mHandler.sendEmptyMessage(MSG_SERVICE_DISCONNECTED); 420 } 421 }; 422 423 /** 424 * Tells the Face Unlock service to start displaying its UI and start processing. 425 */ 426 private void startUi(IBinder windowToken, int x, int y, int w, int h) { 427 if (DEBUG) Log.d(TAG, "startUi()"); 428 synchronized (mServiceRunningLock) { 429 if (!mServiceRunning) { 430 Log.d(TAG, "Starting Face Unlock"); 431 try { 432 mService.startUi(windowToken, x, y, w, h, 433 mLockPatternUtils.isBiometricWeakLivelinessEnabled()); 434 } catch (RemoteException e) { 435 Log.e(TAG, "Caught exception starting Face Unlock: " + e.toString()); 436 return; 437 } 438 mServiceRunning = true; 439 } else { 440 Log.w(TAG, "startUi() attempted while running"); 441 } 442 } 443 } 444 445 /** 446 * Tells the Face Unlock service to stop displaying its UI and stop processing. 447 */ 448 private void stopUi() { 449 if (DEBUG) Log.d(TAG, "stopUi()"); 450 // Note that attempting to stop Face Unlock when it's not running is not an issue. 451 // Face Unlock can return, which stops it and then we try to stop it when the 452 // screen is turned off. That's why we check. 453 synchronized (mServiceRunningLock) { 454 if (mServiceRunning) { 455 Log.d(TAG, "Stopping Face Unlock"); 456 try { 457 mService.stopUi(); 458 } catch (RemoteException e) { 459 Log.e(TAG, "Caught exception stopping Face Unlock: " + e.toString()); 460 } 461 mServiceRunning = false; 462 } else { 463 // This is usually not an error when this happens. Sometimes we will tell it to 464 // stop multiple times because it's called from both onWindowFocusChanged and 465 // onDetachedFromWindow. 466 if (DEBUG) Log.d(TAG, "stopUi() attempted while not running"); 467 } 468 } 469 } 470 471 /** 472 * Implements the AIDL biometric unlock service callback interface. 473 */ 474 private final IFaceLockCallback mFaceUnlockCallback = new IFaceLockCallback.Stub() { 475 /** 476 * Called when Face Unlock wants to grant access to the user. 477 */ 478 public void unlock() { 479 if (DEBUG) Log.d(TAG, "unlock()"); 480 mHandler.sendEmptyMessage(MSG_UNLOCK); 481 } 482 483 /** 484 * Called when Face Unlock wants to go to the backup. 485 */ 486 public void cancel() { 487 if (DEBUG) Log.d(TAG, "cancel()"); 488 mHandler.sendEmptyMessage(MSG_CANCEL); 489 } 490 491 /** 492 * Called when Face Unlock wants to increment the number of failed attempts. 493 */ 494 public void reportFailedAttempt() { 495 if (DEBUG) Log.d(TAG, "reportFailedAttempt()"); 496 mHandler.sendEmptyMessage(MSG_REPORT_FAILED_ATTEMPT); 497 } 498 499 /** 500 * Called when the Face Unlock service starts displaying the UI, indicating that the backup 501 * unlock can be exposed because the Face Unlock service is now covering the backup with its 502 * UI. 503 **/ 504 public void exposeFallback() { 505 if (DEBUG) Log.d(TAG, "exposeFallback()"); 506 mHandler.sendEmptyMessage(MSG_EXPOSE_FALLBACK); 507 } 508 509 /** 510 * Called when Face Unlock wants to keep the screen alive and active for a specific amount 511 * of time. 512 */ 513 public void pokeWakelock(int millis) { 514 if (DEBUG) Log.d(TAG, "pokeWakelock() for " + millis + "ms"); 515 Message message = mHandler.obtainMessage(MSG_POKE_WAKELOCK, millis, -1); 516 mHandler.sendMessage(message); 517 } 518 519 }; 520} 521