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