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 17 package com.android.keyguard; 18 19 import com.android.internal.policy.IFaceLockCallback; 20 import com.android.internal.policy.IFaceLockInterface; 21 import com.android.internal.widget.LockPatternUtils; 22 23 import android.app.admin.DevicePolicyManager; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.ServiceConnection; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Looper; 31 import android.os.Message; 32 import android.os.PowerManager; 33 import android.os.RemoteException; 34 import android.os.UserHandle; 35 import android.util.Log; 36 import android.view.View; 37 38 public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { 39 40 private static final boolean DEBUG = KeyguardConstants.DEBUG; 41 private static final String TAG = "FULLockscreen"; 42 private static final String FACE_LOCK_PACKAGE = "com.android.facelock"; 43 44 private final Context mContext; 45 private final LockPatternUtils mLockPatternUtils; 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_SERVICE_CONNECTED = 0; 58 private final int MSG_SERVICE_DISCONNECTED = 1; 59 private final int MSG_UNLOCK = 2; 60 private final int MSG_CANCEL = 3; 61 private final int MSG_REPORT_FAILED_ATTEMPT = 4; 62 private final int MSG_POKE_WAKELOCK = 5; 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 KeyguardSecurityCallback mKeyguardScreenCallback; 70 71 /** 72 * Stores some of the structures that Face Unlock will need to access and creates the handler 73 * will be used to execute messages on the UI thread. 74 */ FaceUnlock(Context context)75 public FaceUnlock(Context context) { 76 mContext = context; 77 mLockPatternUtils = new LockPatternUtils(context); 78 mHandler = new Handler(this); 79 } 80 setKeyguardCallback(KeyguardSecurityCallback keyguardScreenCallback)81 public void setKeyguardCallback(KeyguardSecurityCallback keyguardScreenCallback) { 82 mKeyguardScreenCallback = keyguardScreenCallback; 83 } 84 85 /** 86 * Stores and displays the view that Face Unlock is allowed to draw within. 87 * TODO: since the layout object will eventually be shared by multiple biometric unlock 88 * methods, we will have to add our other views (background, cancel button) here. 89 */ initializeView(View biometricUnlockView)90 public void initializeView(View biometricUnlockView) { 91 Log.d(TAG, "initializeView()"); 92 mFaceUnlockView = biometricUnlockView; 93 } 94 95 /** 96 * Indicates whether Face Unlock is currently running. 97 */ isRunning()98 public boolean isRunning() { 99 return mIsRunning; 100 } 101 102 /** 103 * Dismisses face unlock and goes to the backup lock 104 */ stopAndShowBackup()105 public void stopAndShowBackup() { 106 if (DEBUG) Log.d(TAG, "stopAndShowBackup()"); 107 mHandler.sendEmptyMessage(MSG_CANCEL); 108 } 109 110 /** 111 * Binds to the Face Unlock service. Face Unlock will be started when the bind completes. The 112 * Face Unlock view is displayed to hide the backup lock while the service is starting up. 113 * Called on the UI thread. 114 */ start()115 public boolean start() { 116 if (DEBUG) Log.d(TAG, "start()"); 117 if (mHandler.getLooper() != Looper.myLooper()) { 118 Log.e(TAG, "start() called off of the UI thread"); 119 } 120 121 if (mIsRunning) { 122 Log.w(TAG, "start() called when already running"); 123 } 124 125 if (!mBoundToService) { 126 Log.d(TAG, "Binding to Face Unlock service for user=" 127 + mLockPatternUtils.getCurrentUser()); 128 mContext.bindServiceAsUser( 129 new Intent(IFaceLockInterface.class.getName()).setPackage(FACE_LOCK_PACKAGE), 130 mConnection, 131 Context.BIND_AUTO_CREATE, 132 new UserHandle(mLockPatternUtils.getCurrentUser())); 133 mBoundToService = true; 134 } else { 135 Log.w(TAG, "Attempt to bind to Face Unlock when already bound"); 136 } 137 138 mIsRunning = true; 139 return true; 140 } 141 142 /** 143 * Stops Face Unlock and unbinds from the service. Called on the UI thread. 144 */ stop()145 public boolean stop() { 146 if (DEBUG) Log.d(TAG, "stop()"); 147 if (mHandler.getLooper() != Looper.myLooper()) { 148 Log.e(TAG, "stop() called from non-UI thread"); 149 } 150 151 // Clearing any old service connected messages. 152 mHandler.removeMessages(MSG_SERVICE_CONNECTED); 153 154 boolean mWasRunning = mIsRunning; 155 156 stopUi(); 157 158 if (mBoundToService) { 159 if (mService != null) { 160 try { 161 mService.unregisterCallback(mFaceUnlockCallback); 162 } catch (RemoteException e) { 163 // Not much we can do 164 } 165 } 166 Log.d(TAG, "Unbinding from Face Unlock service"); 167 mContext.unbindService(mConnection); 168 mBoundToService = false; 169 } else { 170 // This is usually not an error when this happens. Sometimes we will tell it to 171 // unbind multiple times because it's called from both onWindowFocusChanged and 172 // onDetachedFromWindow. 173 if (DEBUG) Log.d(TAG, "Attempt to unbind from Face Unlock when not bound"); 174 } 175 mIsRunning = false; 176 return mWasRunning; 177 } 178 179 /** 180 * Frees up resources used by Face Unlock and stops it if it is still running. 181 */ cleanUp()182 public void cleanUp() { 183 if (DEBUG) Log.d(TAG, "cleanUp()"); 184 if (mService != null) { 185 try { 186 mService.unregisterCallback(mFaceUnlockCallback); 187 } catch (RemoteException e) { 188 // Not much we can do 189 } 190 stopUi(); 191 mService = null; 192 } 193 } 194 195 /** 196 * Returns the Device Policy Manager quality for Face Unlock, which is BIOMETRIC_WEAK. 197 */ getQuality()198 public int getQuality() { 199 return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; 200 } 201 202 /** 203 * Handles messages such that everything happens on the UI thread in a deterministic order. 204 * Calls from the Face Unlock service come from binder threads. Calls from lockscreen typically 205 * come from the UI thread. This makes sure there are no race conditions between those calls. 206 */ handleMessage(Message msg)207 public boolean handleMessage(Message msg) { 208 switch (msg.what) { 209 case MSG_SERVICE_CONNECTED: 210 handleServiceConnected(); 211 break; 212 case MSG_SERVICE_DISCONNECTED: 213 handleServiceDisconnected(); 214 break; 215 case MSG_UNLOCK: 216 handleUnlock(msg.arg1); 217 break; 218 case MSG_CANCEL: 219 handleCancel(); 220 break; 221 case MSG_REPORT_FAILED_ATTEMPT: 222 handleReportFailedAttempt(); 223 break; 224 case MSG_POKE_WAKELOCK: 225 handlePokeWakelock(msg.arg1); 226 break; 227 default: 228 Log.e(TAG, "Unhandled message"); 229 return false; 230 } 231 return true; 232 } 233 234 /** 235 * Tells the service to start its UI via an AIDL interface. Called when the 236 * onServiceConnected() callback is received. 237 */ handleServiceConnected()238 void handleServiceConnected() { 239 Log.d(TAG, "handleServiceConnected()"); 240 241 // It is possible that an unbind has occurred in the time between the bind and when this 242 // function is reached. If an unbind has already occurred, proceeding on to call startUi() 243 // can result in a fatal error. Note that the onServiceConnected() callback is 244 // asynchronous, so this possibility would still exist if we executed this directly in 245 // onServiceConnected() rather than using a handler. 246 if (!mBoundToService) { 247 Log.d(TAG, "Dropping startUi() in handleServiceConnected() because no longer bound"); 248 return; 249 } 250 251 try { 252 mService.registerCallback(mFaceUnlockCallback); 253 } catch (RemoteException e) { 254 Log.e(TAG, "Caught exception connecting to Face Unlock: " + e.toString()); 255 mService = null; 256 mBoundToService = false; 257 mIsRunning = false; 258 return; 259 } 260 261 if (mFaceUnlockView != null) { 262 IBinder windowToken = mFaceUnlockView.getWindowToken(); 263 if (windowToken != null) { 264 // When switching between portrait and landscape view while Face Unlock is running, 265 // the screen will eventually go dark unless we poke the wakelock when Face Unlock 266 // is restarted. 267 mKeyguardScreenCallback.userActivity(); 268 269 int[] position; 270 position = new int[2]; 271 mFaceUnlockView.getLocationInWindow(position); 272 startUi(windowToken, position[0], position[1], mFaceUnlockView.getWidth(), 273 mFaceUnlockView.getHeight()); 274 } else { 275 Log.e(TAG, "windowToken is null in handleServiceConnected()"); 276 } 277 } 278 } 279 280 /** 281 * Called when the onServiceDisconnected() callback is received. This should not happen during 282 * normal operation. It indicates an error has occurred. 283 */ handleServiceDisconnected()284 void handleServiceDisconnected() { 285 Log.e(TAG, "handleServiceDisconnected()"); 286 // TODO: this lock may no longer be needed now that everything is being called from a 287 // handler 288 synchronized (mServiceRunningLock) { 289 mService = null; 290 mServiceRunning = false; 291 } 292 mBoundToService = false; 293 mIsRunning = false; 294 } 295 296 /** 297 * Stops the Face Unlock service and tells the device to grant access to the user. 298 */ handleUnlock(int authenticatedUserId)299 void handleUnlock(int authenticatedUserId) { 300 if (DEBUG) Log.d(TAG, "handleUnlock()"); 301 stop(); 302 int currentUserId = mLockPatternUtils.getCurrentUser(); 303 if (authenticatedUserId == currentUserId) { 304 if (DEBUG) Log.d(TAG, "Unlocking for user " + authenticatedUserId); 305 mKeyguardScreenCallback.reportUnlockAttempt(true); 306 mKeyguardScreenCallback.dismiss(true); 307 } else { 308 Log.d(TAG, "Ignoring unlock for authenticated user (" + authenticatedUserId + 309 ") because the current user is " + currentUserId); 310 } 311 } 312 313 /** 314 * Stops the Face Unlock service and goes to the backup lock. 315 */ handleCancel()316 void handleCancel() { 317 if (DEBUG) Log.d(TAG, "handleCancel()"); 318 // We are going to the backup method, so we don't want to see Face Unlock again until the 319 // next time the user visits keyguard. 320 KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false); 321 322 mKeyguardScreenCallback.showBackupSecurity(); 323 stop(); 324 mKeyguardScreenCallback.userActivity(); 325 } 326 327 /** 328 * Increments the number of failed Face Unlock attempts. 329 */ handleReportFailedAttempt()330 void handleReportFailedAttempt() { 331 if (DEBUG) Log.d(TAG, "handleReportFailedAttempt()"); 332 // We are going to the backup method, so we don't want to see Face Unlock again until the 333 // next time the user visits keyguard. 334 KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false); 335 336 mKeyguardScreenCallback.reportUnlockAttempt(false); 337 } 338 339 /** 340 * If the screen is on, pokes the wakelock to keep the screen alive and active for a specific 341 * amount of time. 342 */ handlePokeWakelock(int millis)343 void handlePokeWakelock(int millis) { 344 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 345 if (powerManager.isScreenOn()) { 346 mKeyguardScreenCallback.userActivity(); 347 } 348 } 349 350 /** 351 * Implements service connection methods. 352 */ 353 private ServiceConnection mConnection = new ServiceConnection() { 354 /** 355 * Called when the Face Unlock service connects after calling bind(). 356 */ 357 public void onServiceConnected(ComponentName className, IBinder iservice) { 358 Log.d(TAG, "Connected to Face Unlock service"); 359 mService = IFaceLockInterface.Stub.asInterface(iservice); 360 mHandler.sendEmptyMessage(MSG_SERVICE_CONNECTED); 361 } 362 363 /** 364 * Called if the Face Unlock service unexpectedly disconnects. This indicates an error. 365 */ 366 public void onServiceDisconnected(ComponentName className) { 367 Log.e(TAG, "Unexpected disconnect from Face Unlock service"); 368 mHandler.sendEmptyMessage(MSG_SERVICE_DISCONNECTED); 369 } 370 }; 371 372 /** 373 * Tells the Face Unlock service to start displaying its UI and start processing. 374 */ startUi(IBinder windowToken, int x, int y, int w, int h)375 private void startUi(IBinder windowToken, int x, int y, int w, int h) { 376 if (DEBUG) Log.d(TAG, "startUi()"); 377 synchronized (mServiceRunningLock) { 378 if (!mServiceRunning) { 379 Log.d(TAG, "Starting Face Unlock"); 380 try { 381 mService.startUi(windowToken, x, y, w, h, 382 mLockPatternUtils.isBiometricWeakLivelinessEnabled()); 383 } catch (RemoteException e) { 384 Log.e(TAG, "Caught exception starting Face Unlock: " + e.toString()); 385 return; 386 } 387 mServiceRunning = true; 388 } else { 389 Log.w(TAG, "startUi() attempted while running"); 390 } 391 } 392 } 393 394 /** 395 * Tells the Face Unlock service to stop displaying its UI and stop processing. 396 */ stopUi()397 private void stopUi() { 398 if (DEBUG) Log.d(TAG, "stopUi()"); 399 // Note that attempting to stop Face Unlock when it's not running is not an issue. 400 // Face Unlock can return, which stops it and then we try to stop it when the 401 // screen is turned off. That's why we check. 402 synchronized (mServiceRunningLock) { 403 if (mServiceRunning) { 404 Log.d(TAG, "Stopping Face Unlock"); 405 try { 406 mService.stopUi(); 407 } catch (RemoteException e) { 408 Log.e(TAG, "Caught exception stopping Face Unlock: " + e.toString()); 409 } 410 mServiceRunning = false; 411 } else { 412 // This is usually not an error when this happens. Sometimes we will tell it to 413 // stop multiple times because it's called from both onWindowFocusChanged and 414 // onDetachedFromWindow. 415 if (DEBUG) Log.d(TAG, "stopUi() attempted while not running"); 416 } 417 } 418 } 419 420 /** 421 * Implements the AIDL biometric unlock service callback interface. 422 */ 423 private final IFaceLockCallback mFaceUnlockCallback = new IFaceLockCallback.Stub() { 424 /** 425 * Called when Face Unlock wants to grant access to the user. 426 */ 427 public void unlock() { 428 if (DEBUG) Log.d(TAG, "unlock()"); 429 Message message = mHandler.obtainMessage(MSG_UNLOCK, UserHandle.getCallingUserId(), -1); 430 mHandler.sendMessage(message); 431 } 432 433 /** 434 * Called when Face Unlock wants to go to the backup. 435 */ 436 public void cancel() { 437 if (DEBUG) Log.d(TAG, "cancel()"); 438 mHandler.sendEmptyMessage(MSG_CANCEL); 439 } 440 441 /** 442 * Called when Face Unlock wants to increment the number of failed attempts. 443 */ 444 public void reportFailedAttempt() { 445 if (DEBUG) Log.d(TAG, "reportFailedAttempt()"); 446 mHandler.sendEmptyMessage(MSG_REPORT_FAILED_ATTEMPT); 447 } 448 449 /** 450 * Called when Face Unlock wants to keep the screen alive and active for a specific amount 451 * of time. 452 */ 453 public void pokeWakelock(int millis) { 454 if (DEBUG) Log.d(TAG, "pokeWakelock() for " + millis + "ms"); 455 Message message = mHandler.obtainMessage(MSG_POKE_WAKELOCK, millis, -1); 456 mHandler.sendMessage(message); 457 } 458 459 }; 460 } 461