Fields Summary |
---|
private static final boolean | DEBUG |
private static final String | TAG |
private static final String | FACE_LOCK_PACKAGE |
private final android.content.Context | mContext |
private final com.android.internal.widget.LockPatternUtils | mLockPatternUtils |
private boolean | mServiceRunning |
private final Object | mServiceRunningLock |
private com.android.internal.policy.IFaceLockInterface | mService |
private boolean | mBoundToService |
private android.view.View | mFaceUnlockView |
private android.os.Handler | mHandler |
private final int | MSG_SERVICE_CONNECTED |
private final int | MSG_SERVICE_DISCONNECTED |
private final int | MSG_UNLOCK |
private final int | MSG_CANCEL |
private final int | MSG_REPORT_FAILED_ATTEMPT |
private final int | MSG_POKE_WAKELOCK |
private volatile boolean | mIsRunning |
KeyguardSecurityCallback | mKeyguardScreenCallback |
private android.content.ServiceConnection | mConnectionImplements service connection methods. |
private final com.android.internal.policy.IFaceLockCallback | mFaceUnlockCallbackImplements the AIDL biometric unlock service callback interface. |
Methods Summary |
---|
public void | cleanUp()Frees up resources used by Face Unlock and stops it if it is still running.
if (DEBUG) Log.d(TAG, "cleanUp()");
if (mService != null) {
try {
mService.unregisterCallback(mFaceUnlockCallback);
} catch (RemoteException e) {
// Not much we can do
}
stopUi();
mService = null;
}
|
public int | getQuality()Returns the Device Policy Manager quality for Face Unlock, which is BIOMETRIC_WEAK.
return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
|
void | handleCancel()Stops the Face Unlock service and goes to the backup lock.
if (DEBUG) Log.d(TAG, "handleCancel()");
// We are going to the backup method, so we don't want to see Face Unlock again until the
// next time the user visits keyguard.
KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
mKeyguardScreenCallback.showBackupSecurity();
stop();
mKeyguardScreenCallback.userActivity();
|
public boolean | handleMessage(android.os.Message msg)Handles messages such that everything happens on the UI thread in a deterministic order.
Calls from the Face Unlock service come from binder threads. Calls from lockscreen typically
come from the UI thread. This makes sure there are no race conditions between those calls.
switch (msg.what) {
case MSG_SERVICE_CONNECTED:
handleServiceConnected();
break;
case MSG_SERVICE_DISCONNECTED:
handleServiceDisconnected();
break;
case MSG_UNLOCK:
handleUnlock(msg.arg1);
break;
case MSG_CANCEL:
handleCancel();
break;
case MSG_REPORT_FAILED_ATTEMPT:
handleReportFailedAttempt();
break;
case MSG_POKE_WAKELOCK:
handlePokeWakelock(msg.arg1);
break;
default:
Log.e(TAG, "Unhandled message");
return false;
}
return true;
|
void | handlePokeWakelock(int millis)If the screen is on, pokes the wakelock to keep the screen alive and active for a specific
amount of time.
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
if (powerManager.isScreenOn()) {
mKeyguardScreenCallback.userActivity();
}
|
void | handleReportFailedAttempt()Increments the number of failed Face Unlock attempts.
if (DEBUG) Log.d(TAG, "handleReportFailedAttempt()");
// We are going to the backup method, so we don't want to see Face Unlock again until the
// next time the user visits keyguard.
KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
mKeyguardScreenCallback.reportUnlockAttempt(false);
|
void | handleServiceConnected()Tells the service to start its UI via an AIDL interface. Called when the
onServiceConnected() callback is received.
Log.d(TAG, "handleServiceConnected()");
// It is possible that an unbind has occurred in the time between the bind and when this
// function is reached. If an unbind has already occurred, proceeding on to call startUi()
// can result in a fatal error. Note that the onServiceConnected() callback is
// asynchronous, so this possibility would still exist if we executed this directly in
// onServiceConnected() rather than using a handler.
if (!mBoundToService) {
Log.d(TAG, "Dropping startUi() in handleServiceConnected() because no longer bound");
return;
}
try {
mService.registerCallback(mFaceUnlockCallback);
} catch (RemoteException e) {
Log.e(TAG, "Caught exception connecting to Face Unlock: " + e.toString());
mService = null;
mBoundToService = false;
mIsRunning = false;
return;
}
if (mFaceUnlockView != null) {
IBinder windowToken = mFaceUnlockView.getWindowToken();
if (windowToken != null) {
// When switching between portrait and landscape view while Face Unlock is running,
// the screen will eventually go dark unless we poke the wakelock when Face Unlock
// is restarted.
mKeyguardScreenCallback.userActivity();
int[] position;
position = new int[2];
mFaceUnlockView.getLocationInWindow(position);
startUi(windowToken, position[0], position[1], mFaceUnlockView.getWidth(),
mFaceUnlockView.getHeight());
} else {
Log.e(TAG, "windowToken is null in handleServiceConnected()");
}
}
|
void | handleServiceDisconnected()Called when the onServiceDisconnected() callback is received. This should not happen during
normal operation. It indicates an error has occurred.
Log.e(TAG, "handleServiceDisconnected()");
// TODO: this lock may no longer be needed now that everything is being called from a
// handler
synchronized (mServiceRunningLock) {
mService = null;
mServiceRunning = false;
}
mBoundToService = false;
mIsRunning = false;
|
void | handleUnlock(int authenticatedUserId)Stops the Face Unlock service and tells the device to grant access to the user.
if (DEBUG) Log.d(TAG, "handleUnlock()");
stop();
int currentUserId = mLockPatternUtils.getCurrentUser();
if (authenticatedUserId == currentUserId) {
if (DEBUG) Log.d(TAG, "Unlocking for user " + authenticatedUserId);
mKeyguardScreenCallback.reportUnlockAttempt(true);
mKeyguardScreenCallback.dismiss(true);
} else {
Log.d(TAG, "Ignoring unlock for authenticated user (" + authenticatedUserId +
") because the current user is " + currentUserId);
}
|
public void | initializeView(android.view.View biometricUnlockView)Stores and displays the view that Face Unlock is allowed to draw within.
TODO: since the layout object will eventually be shared by multiple biometric unlock
methods, we will have to add our other views (background, cancel button) here.
Log.d(TAG, "initializeView()");
mFaceUnlockView = biometricUnlockView;
|
public boolean | isRunning()Indicates whether Face Unlock is currently running.
return mIsRunning;
|
public void | setKeyguardCallback(KeyguardSecurityCallback keyguardScreenCallback)
mKeyguardScreenCallback = keyguardScreenCallback;
|
public boolean | start()Binds to the Face Unlock service. Face Unlock will be started when the bind completes. The
Face Unlock view is displayed to hide the backup lock while the service is starting up.
Called on the UI thread.
if (DEBUG) Log.d(TAG, "start()");
if (mHandler.getLooper() != Looper.myLooper()) {
Log.e(TAG, "start() called off of the UI thread");
}
if (mIsRunning) {
Log.w(TAG, "start() called when already running");
}
if (!mBoundToService) {
Log.d(TAG, "Binding to Face Unlock service for user="
+ mLockPatternUtils.getCurrentUser());
mContext.bindServiceAsUser(
new Intent(IFaceLockInterface.class.getName()).setPackage(FACE_LOCK_PACKAGE),
mConnection,
Context.BIND_AUTO_CREATE,
new UserHandle(mLockPatternUtils.getCurrentUser()));
mBoundToService = true;
} else {
Log.w(TAG, "Attempt to bind to Face Unlock when already bound");
}
mIsRunning = true;
return true;
|
private void | startUi(android.os.IBinder windowToken, int x, int y, int w, int h)Tells the Face Unlock service to start displaying its UI and start processing.
if (DEBUG) Log.d(TAG, "startUi()");
synchronized (mServiceRunningLock) {
if (!mServiceRunning) {
Log.d(TAG, "Starting Face Unlock");
try {
mService.startUi(windowToken, x, y, w, h,
mLockPatternUtils.isBiometricWeakLivelinessEnabled());
} catch (RemoteException e) {
Log.e(TAG, "Caught exception starting Face Unlock: " + e.toString());
return;
}
mServiceRunning = true;
} else {
Log.w(TAG, "startUi() attempted while running");
}
}
|
public boolean | stop()Stops Face Unlock and unbinds from the service. Called on the UI thread.
if (DEBUG) Log.d(TAG, "stop()");
if (mHandler.getLooper() != Looper.myLooper()) {
Log.e(TAG, "stop() called from non-UI thread");
}
// Clearing any old service connected messages.
mHandler.removeMessages(MSG_SERVICE_CONNECTED);
boolean mWasRunning = mIsRunning;
stopUi();
if (mBoundToService) {
if (mService != null) {
try {
mService.unregisterCallback(mFaceUnlockCallback);
} catch (RemoteException e) {
// Not much we can do
}
}
Log.d(TAG, "Unbinding from Face Unlock service");
mContext.unbindService(mConnection);
mBoundToService = false;
} else {
// This is usually not an error when this happens. Sometimes we will tell it to
// unbind multiple times because it's called from both onWindowFocusChanged and
// onDetachedFromWindow.
if (DEBUG) Log.d(TAG, "Attempt to unbind from Face Unlock when not bound");
}
mIsRunning = false;
return mWasRunning;
|
public void | stopAndShowBackup()Dismisses face unlock and goes to the backup lock
if (DEBUG) Log.d(TAG, "stopAndShowBackup()");
mHandler.sendEmptyMessage(MSG_CANCEL);
|
private void | stopUi()Tells the Face Unlock service to stop displaying its UI and stop processing.
if (DEBUG) Log.d(TAG, "stopUi()");
// Note that attempting to stop Face Unlock when it's not running is not an issue.
// Face Unlock can return, which stops it and then we try to stop it when the
// screen is turned off. That's why we check.
synchronized (mServiceRunningLock) {
if (mServiceRunning) {
Log.d(TAG, "Stopping Face Unlock");
try {
mService.stopUi();
} catch (RemoteException e) {
Log.e(TAG, "Caught exception stopping Face Unlock: " + e.toString());
}
mServiceRunning = false;
} else {
// This is usually not an error when this happens. Sometimes we will tell it to
// stop multiple times because it's called from both onWindowFocusChanged and
// onDetachedFromWindow.
if (DEBUG) Log.d(TAG, "stopUi() attempted while not running");
}
}
|