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