Merge "Changes to biometric sensor interface in lockscreen" into jb-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
6bc7591640
@@ -19,33 +19,63 @@ package com.android.internal.policy.impl;
|
||||
import android.view.View;
|
||||
|
||||
interface BiometricSensorUnlock {
|
||||
// Returns 'true' if the biometric sensor has started its unlock procedure but has not yet
|
||||
// accepted or rejected the user.
|
||||
/**
|
||||
* Initializes the view provided for the biometric unlock UI to work within. The provided area
|
||||
* completely covers the backup unlock mechanism. The view is then displayed in the same manner
|
||||
* as if {@link BiometricSensorUnlock#show(long)} was called with a timeout of 0.
|
||||
* @param biometricUnlockView View provided for the biometric unlock UI.
|
||||
*/
|
||||
public void initializeView(View biometricUnlockView);
|
||||
|
||||
/**
|
||||
* Indicates whether the biometric unlock is running. Before
|
||||
* {@link BiometricSensorUnlock#start} is called, isRunning() returns false. After a successful
|
||||
* call to {@link BiometricSensorUnlock#start}, isRunning() returns true until the biometric
|
||||
* unlock completes, {@link BiometricSensorUnlock#stop} has been called, or an error has
|
||||
* forced the biometric unlock to stop.
|
||||
* @return whether the biometric unlock is currently running.
|
||||
*/
|
||||
public boolean isRunning();
|
||||
|
||||
// Show the interface, but don't start the unlock procedure. The interface should disappear
|
||||
// after the specified timeout. If the timeout is 0, the interface shows until another event,
|
||||
// such as calling hide(), causes it to disappear.
|
||||
// Called on the UI Thread
|
||||
/**
|
||||
* Covers the backup unlock mechanism by showing the contents of the view initialized in
|
||||
* {@link BiometricSensorUnlock#initializeView(View)}. The view should disappear after the
|
||||
* specified timeout. If the timeout is 0, the interface shows until another event, such as
|
||||
* calling {@link BiometricSensorUnlock#hide()}, causes it to disappear. Called on the UI
|
||||
* thread.
|
||||
* @param timeoutMilliseconds Amount of time in milliseconds to display the view before
|
||||
* disappearing. A value of 0 means the view should remain visible.
|
||||
*/
|
||||
public void show(long timeoutMilliseconds);
|
||||
|
||||
// Hide the interface, if any, exposing the lockscreen.
|
||||
/**
|
||||
* Uncovers the backup unlock mechanism by hiding the contents of the view initialized in
|
||||
* {@link BiometricSensorUnlock#initializeView(View)}.
|
||||
*/
|
||||
public void hide();
|
||||
|
||||
// Stop the unlock procedure if running. Returns 'true' if it was in fact running.
|
||||
/**
|
||||
* Binds to the biometric unlock service and starts the unlock procedure. Called on the UI
|
||||
* thread.
|
||||
* @return false if it can't be started or the backup should be used.
|
||||
*/
|
||||
public boolean start();
|
||||
|
||||
/**
|
||||
* Stops the biometric unlock procedure and unbinds from the service.
|
||||
* @return whether the biometric unlock was running when called.
|
||||
*/
|
||||
public boolean stop();
|
||||
|
||||
// Start the unlock procedure. Returns ‘false’ if it can’t be started or if the backup should
|
||||
// be used.
|
||||
// Called on the UI thread.
|
||||
public boolean start(boolean suppressBiometricUnlock);
|
||||
|
||||
// Provide a view to work within.
|
||||
public void initializeAreaView(View topView);
|
||||
|
||||
// Clean up any resources used by the biometric unlock.
|
||||
/**
|
||||
* Cleans up any resources used by the biometric unlock.
|
||||
*/
|
||||
public void cleanUp();
|
||||
|
||||
// Returns the Device Policy Manager quality (e.g. PASSWORD_QUALITY_BIOMETRIC_WEAK).
|
||||
/**
|
||||
* Gets the Device Policy Manager quality of the biometric unlock sensor
|
||||
* (e.g., PASSWORD_QUALITY_BIOMETRIC_WEAK).
|
||||
* @return biometric unlock sensor quality, as defined by Device Policy Manager.
|
||||
*/
|
||||
public int getQuality();
|
||||
}
|
||||
|
||||
@@ -40,31 +40,34 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
|
||||
private static final String TAG = "FULLockscreen";
|
||||
|
||||
private final Context mContext;
|
||||
private final LockPatternUtils mLockPatternUtils;
|
||||
private final KeyguardUpdateMonitor mUpdateMonitor;
|
||||
|
||||
// TODO: is mServiceRunning needed or can we just use mIsRunning or check if mService is null?
|
||||
private boolean mServiceRunning = false;
|
||||
private final Object mServiceRunningLock = new Object();
|
||||
private IFaceLockInterface mService;
|
||||
private boolean mBoundToService = false;
|
||||
private View mAreaView;
|
||||
private View mFaceUnlockView;
|
||||
|
||||
private Handler mHandler;
|
||||
private final int MSG_SHOW_AREA_VIEW = 0;
|
||||
private final int MSG_HIDE_AREA_VIEW = 1;
|
||||
|
||||
private boolean mServiceRunning = false;
|
||||
private final Object mServiceRunningLock = new Object();
|
||||
// TODO: This was added for the purpose of adhering to what the biometric interface expects
|
||||
// the isRunning() function to return. However, it is probably not necessary to have both
|
||||
// mRunning and mServiceRunning. I'd just rather wait to change that logic.
|
||||
private boolean mIsRunning = false;
|
||||
|
||||
// Long enough to stay visible while the service starts
|
||||
// Short enough to not have to wait long for backup if service fails to start or crashes
|
||||
// The service can take a couple of seconds to start on the first try after boot
|
||||
private final int VIEW_AREA_SERVICE_TIMEOUT = 3000;
|
||||
|
||||
// So the user has a consistent amount of time when brought to the backup method from FaceLock
|
||||
// So the user has a consistent amount of time when brought to the backup method from Face
|
||||
// Unlock
|
||||
private final int BACKUP_LOCK_TIMEOUT = 5000;
|
||||
|
||||
/**
|
||||
* Used to lookup the state of the lock pattern
|
||||
*/
|
||||
private final LockPatternUtils mLockPatternUtils;
|
||||
|
||||
KeyguardScreenCallback mKeyguardScreenCallback;
|
||||
|
||||
@@ -77,102 +80,116 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
|
||||
mHandler = new Handler(this);
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return mServiceRunning;
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void initializeView(View biometricUnlockView) {
|
||||
mFaceUnlockView = biometricUnlockView;
|
||||
show(0);
|
||||
}
|
||||
|
||||
// Shows the FaceLock area for a period of time
|
||||
// Called on the UI thread
|
||||
/**
|
||||
* Indicates whether Face Unlock is currently running.
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return mIsRunning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Face Unlock view to visible, hiding it after the specified amount of time. If
|
||||
* timeoutMillis is 0, no hide is performed.
|
||||
*/
|
||||
public void show(long timeoutMillis) {
|
||||
removeAreaDisplayMessages();
|
||||
showArea();
|
||||
if (timeoutMillis > 0)
|
||||
if (mFaceUnlockView != null) {
|
||||
mFaceUnlockView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (timeoutMillis > 0) {
|
||||
mHandler.sendEmptyMessageDelayed(MSG_HIDE_AREA_VIEW, timeoutMillis);
|
||||
}
|
||||
}
|
||||
|
||||
// Hides the FaceLock area immediately
|
||||
/**
|
||||
* Hides the Face Unlock view.
|
||||
*/
|
||||
public void hide() {
|
||||
// Remove messages to prevent a delayed show message from undo-ing the hide
|
||||
removeAreaDisplayMessages();
|
||||
mHandler.sendEmptyMessage(MSG_HIDE_AREA_VIEW);
|
||||
}
|
||||
|
||||
// Tells FaceLock to stop and then unbinds from the FaceLock service
|
||||
/**
|
||||
* Binds to the Face Unlock service. Face Unlock will be started when the bind completes. The
|
||||
* Face Unlock area is displayed to hide the backup while the service is starting up.
|
||||
*/
|
||||
public boolean start() {
|
||||
if (mIsRunning) {
|
||||
Log.w(TAG, "start() called when already running");
|
||||
}
|
||||
|
||||
// Show Face Unlock view, but only for a little bit so lockpattern will become visible if
|
||||
// Face Unlock fails to start or crashes
|
||||
// This must show before bind to guarantee that Face Unlock has a place to display
|
||||
show(VIEW_AREA_SERVICE_TIMEOUT);
|
||||
if (!mBoundToService) {
|
||||
if (DEBUG) Log.d(TAG, "before bind to Face Unlock service");
|
||||
mContext.bindService(new Intent(IFaceLockInterface.class.getName()),
|
||||
mConnection,
|
||||
Context.BIND_AUTO_CREATE,
|
||||
mLockPatternUtils.getCurrentUser());
|
||||
if (DEBUG) Log.d(TAG, "after bind to Face Unlock service");
|
||||
mBoundToService = true;
|
||||
} else {
|
||||
Log.w(TAG, "Attempt to bind to Face Unlock when already bound");
|
||||
}
|
||||
|
||||
// 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.pokeWakelock();
|
||||
|
||||
mIsRunning = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops Face Unlock and unbinds from the service.
|
||||
*/
|
||||
public boolean stop() {
|
||||
boolean wasRunning = false;
|
||||
boolean mWasRunning = mIsRunning;
|
||||
stopUi();
|
||||
|
||||
if (mBoundToService) {
|
||||
wasRunning = true;
|
||||
if (DEBUG) Log.d(TAG, "before unbind from FaceLock service");
|
||||
if (DEBUG) Log.d(TAG, "before unbind from Face Unlock service");
|
||||
if (mService != null) {
|
||||
try {
|
||||
mService.unregisterCallback(mFaceLockCallback);
|
||||
mService.unregisterCallback(mFaceUnlockCallback);
|
||||
} catch (RemoteException e) {
|
||||
// Not much we can do
|
||||
}
|
||||
}
|
||||
mContext.unbindService(mConnection);
|
||||
if (DEBUG) Log.d(TAG, "after unbind from FaceLock service");
|
||||
if (DEBUG) Log.d(TAG, "after unbind from Face Unlock service");
|
||||
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 FaceLock when not bound");
|
||||
if (DEBUG) Log.d(TAG, "Attempt to unbind from Face Unlock when not bound");
|
||||
}
|
||||
|
||||
return wasRunning;
|
||||
mIsRunning = false;
|
||||
return mWasRunning;
|
||||
}
|
||||
|
||||
/**
|
||||
* When screen is turned on and focused, need to bind to FaceLock service if we are using
|
||||
* FaceLock, but only if we're not dealing with a call
|
||||
* Called on the UI thread
|
||||
* Frees up resources used by Face Unlock and stops it if it is still running.
|
||||
*/
|
||||
public boolean start(boolean suppressBiometricUnlock) {
|
||||
final boolean tooManyFaceUnlockTries = mUpdateMonitor.getMaxFaceUnlockAttemptsReached();
|
||||
final int failedBackupAttempts = mUpdateMonitor.getFailedAttempts();
|
||||
final boolean backupIsTimedOut =
|
||||
(failedBackupAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
|
||||
if (tooManyFaceUnlockTries) Log.i(TAG, "tooManyFaceUnlockTries: " + tooManyFaceUnlockTries);
|
||||
if (mUpdateMonitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
|
||||
&& !suppressBiometricUnlock
|
||||
&& !tooManyFaceUnlockTries
|
||||
&& !backupIsTimedOut) {
|
||||
// Show FaceLock area, but only for a little bit so lockpattern will become visible if
|
||||
// FaceLock fails to start or crashes
|
||||
// This must show before bind to guarantee that Face Unlock has a place to display
|
||||
show(VIEW_AREA_SERVICE_TIMEOUT);
|
||||
|
||||
bind();
|
||||
|
||||
// When switching between portrait and landscape view while FaceLock is running, the
|
||||
// screen will eventually go dark unless we poke the wakelock when FaceLock is
|
||||
// restarted
|
||||
mKeyguardScreenCallback.pokeWakelock();
|
||||
} else {
|
||||
hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Takes care of FaceLock area when layout is created
|
||||
public void initializeAreaView(View topView) {
|
||||
mAreaView = topView.findViewById(R.id.faceLockAreaView);
|
||||
if (mAreaView == null) {
|
||||
Log.e(TAG, "Layout does not have areaView and FaceLock is enabled");
|
||||
} else {
|
||||
show(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanUp() {
|
||||
if (mService != null) {
|
||||
try {
|
||||
mService.unregisterCallback(mFaceLockCallback);
|
||||
mService.unregisterCallback(mFaceUnlockCallback);
|
||||
} catch (RemoteException e) {
|
||||
// Not much we can do
|
||||
}
|
||||
@@ -181,30 +198,31 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Device Policy Manager quality for Face Unlock, which is BIOMETRIC_WEAK.
|
||||
*/
|
||||
public int getQuality() {
|
||||
return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
|
||||
}
|
||||
|
||||
// Shows the FaceLock area
|
||||
// Called on the UI thread
|
||||
private void showArea() {
|
||||
if (mAreaView != null) {
|
||||
mAreaView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
// Handles covering or exposing FaceLock area on the client side when FaceLock starts or stops
|
||||
// This needs to be done in a handler because the call could be coming from a callback from the
|
||||
// FaceLock service that is in a thread that can't modify the UI
|
||||
/**
|
||||
* Handles showing the Face Unlock view (hiding the backup lock) and hiding the Face Unlock view
|
||||
* (exposing the backup lock). In cases where 'show' needs to happen immediately,
|
||||
* setVisibility() is called directly (without using this handler). This handler is used when
|
||||
* 'show' needs to happen from a non-UI thread. It also handles hide() messages since they
|
||||
* often require a delay.
|
||||
*/
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_SHOW_AREA_VIEW:
|
||||
showArea();
|
||||
if (mFaceUnlockView != null) {
|
||||
mFaceUnlockView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
break;
|
||||
case MSG_HIDE_AREA_VIEW:
|
||||
if (mAreaView != null) {
|
||||
mAreaView.setVisibility(View.INVISIBLE);
|
||||
if (mFaceUnlockView != null) {
|
||||
mFaceUnlockView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -214,53 +232,44 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Removes show and hide messages from the message queue
|
||||
/**
|
||||
* Removes show and hide messages from the message queue
|
||||
*/
|
||||
private void removeAreaDisplayMessages() {
|
||||
mHandler.removeMessages(MSG_SHOW_AREA_VIEW);
|
||||
mHandler.removeMessages(MSG_HIDE_AREA_VIEW);
|
||||
}
|
||||
|
||||
// Binds to FaceLock service. This call does not tell it to start, but it causes the service
|
||||
// to call the onServiceConnected callback, which then starts FaceLock.
|
||||
private void bind() {
|
||||
if (!mBoundToService) {
|
||||
if (DEBUG) Log.d(TAG, "before bind to FaceLock service");
|
||||
mContext.bindService(new Intent(IFaceLockInterface.class.getName()),
|
||||
mConnection,
|
||||
Context.BIND_AUTO_CREATE,
|
||||
mLockPatternUtils.getCurrentUser());
|
||||
if (DEBUG) Log.d(TAG, "after bind to FaceLock service");
|
||||
mBoundToService = true;
|
||||
} else {
|
||||
Log.w(TAG, "Attempt to bind to FaceLock when already bound");
|
||||
}
|
||||
}
|
||||
|
||||
private ServiceConnection mConnection = new ServiceConnection() {
|
||||
// Completes connection, registers callback and starts FaceLock when service is bound
|
||||
/**
|
||||
* Completes connection, registers callback, and starts Face Unlock when service is bound
|
||||
*/
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder iservice) {
|
||||
mService = IFaceLockInterface.Stub.asInterface(iservice);
|
||||
if (DEBUG) Log.d(TAG, "Connected to FaceLock service");
|
||||
if (DEBUG) Log.d(TAG, "Connected to Face Unlock service");
|
||||
try {
|
||||
mService.registerCallback(mFaceLockCallback);
|
||||
mService.registerCallback(mFaceUnlockCallback);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Caught exception connecting to FaceLock: " + e.toString());
|
||||
Log.e(TAG, "Caught exception connecting to Face Unlock: " + e.toString());
|
||||
mService = null;
|
||||
mBoundToService = false;
|
||||
mIsRunning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAreaView != null) {
|
||||
if (mFaceUnlockView != null) {
|
||||
int[] position;
|
||||
position = new int[2];
|
||||
mAreaView.getLocationInWindow(position);
|
||||
startUi(mAreaView.getWindowToken(), position[0], position[1],
|
||||
mAreaView.getWidth(), mAreaView.getHeight());
|
||||
mFaceUnlockView.getLocationInWindow(position);
|
||||
startUi(mFaceUnlockView.getWindowToken(), position[0], position[1],
|
||||
mFaceUnlockView.getWidth(), mFaceUnlockView.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
// Cleans up if FaceLock service unexpectedly disconnects
|
||||
/**
|
||||
* Cleans up if Face Unlock service unexpectedly disconnects
|
||||
*/
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
synchronized(mServiceRunningLock) {
|
||||
@@ -268,20 +277,23 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
|
||||
mServiceRunning = false;
|
||||
}
|
||||
mBoundToService = false;
|
||||
Log.w(TAG, "Unexpected disconnect from FaceLock service");
|
||||
mIsRunning = false;
|
||||
Log.w(TAG, "Unexpected disconnect from Face Unlock service");
|
||||
}
|
||||
};
|
||||
|
||||
// Tells the FaceLock service to start displaying its UI and perform recognition
|
||||
/**
|
||||
* Tells the Face Unlock service to start displaying its UI and perform recognition
|
||||
*/
|
||||
private void startUi(IBinder windowToken, int x, int y, int w, int h) {
|
||||
synchronized (mServiceRunningLock) {
|
||||
if (!mServiceRunning) {
|
||||
if (DEBUG) Log.d(TAG, "Starting FaceLock");
|
||||
if (DEBUG) 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 FaceLock: " + e.toString());
|
||||
Log.e(TAG, "Caught exception starting Face Unlock: " + e.toString());
|
||||
return;
|
||||
}
|
||||
mServiceRunning = true;
|
||||
@@ -291,30 +303,36 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
// Tells the FaceLock service to stop displaying its UI and stop recognition
|
||||
/**
|
||||
* Tells the Face Unlock service to stop displaying its UI and stop recognition
|
||||
*/
|
||||
private void stopUi() {
|
||||
// Note that attempting to stop FaceLock when it's not running is not an issue.
|
||||
// FaceLock can return, which stops it and then we try to stop it when the
|
||||
// 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) {
|
||||
try {
|
||||
if (DEBUG) Log.d(TAG, "Stopping FaceLock");
|
||||
if (DEBUG) Log.d(TAG, "Stopping Face Unlock");
|
||||
mService.stopUi();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Caught exception stopping FaceLock: " + e.toString());
|
||||
Log.e(TAG, "Caught exception stopping Face Unlock: " + e.toString());
|
||||
}
|
||||
mServiceRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the FaceLock service callback interface defined in AIDL
|
||||
private final IFaceLockCallback mFaceLockCallback = new IFaceLockCallback.Stub() {
|
||||
// Stops the FaceLock UI and indicates that the phone should be unlocked
|
||||
/**
|
||||
* Implements the biometric unlock service callback interface defined in AIDL
|
||||
*/
|
||||
private final IFaceLockCallback mFaceUnlockCallback = new IFaceLockCallback.Stub() {
|
||||
/**
|
||||
* Stops the Face Unlock UI and indicates that the phone should be unlocked
|
||||
*/
|
||||
@Override
|
||||
public void unlock() {
|
||||
if (DEBUG) Log.d(TAG, "FaceLock unlock()");
|
||||
if (DEBUG) Log.d(TAG, "unlock()");
|
||||
|
||||
// Keep fallback covered
|
||||
removeAreaDisplayMessages();
|
||||
@@ -326,38 +344,46 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
|
||||
mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
|
||||
}
|
||||
|
||||
// Stops the FaceLock UI and exposes the backup method without unlocking
|
||||
// This means the user has cancelled out
|
||||
/**
|
||||
* Stops the Face Unlock UI and exposes the backup method without unlocking
|
||||
* This means the user has cancelled out
|
||||
*/
|
||||
@Override
|
||||
public void cancel() {
|
||||
if (DEBUG) Log.d(TAG, "FaceLock cancel()");
|
||||
if (DEBUG) Log.d(TAG, "cancel()");
|
||||
hide(); // Expose fallback
|
||||
stop();
|
||||
mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT);
|
||||
}
|
||||
|
||||
// Stops the FaceLock UI and exposes the backup method without unlocking
|
||||
// This means FaceLock failed to recognize them
|
||||
/**
|
||||
* Stops the Face Unlock UI and exposes the backup method without unlocking
|
||||
* This means Face Unlock failed to recognize them
|
||||
*/
|
||||
@Override
|
||||
public void reportFailedAttempt() {
|
||||
if (DEBUG) Log.d(TAG, "FaceLock reportFailedAttempt()");
|
||||
mUpdateMonitor.reportFailedFaceUnlockAttempt();
|
||||
if (DEBUG) Log.d(TAG, "reportFailedAttempt()");
|
||||
mUpdateMonitor.reportFailedBiometricUnlockAttempt();
|
||||
hide(); // Expose fallback
|
||||
stop();
|
||||
mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT);
|
||||
}
|
||||
|
||||
// Removes the black area that covers the backup unlock method
|
||||
/**
|
||||
* Removes the black area that covers the backup unlock method
|
||||
**/
|
||||
@Override
|
||||
public void exposeFallback() {
|
||||
if (DEBUG) Log.d(TAG, "FaceLock exposeFallback()");
|
||||
if (DEBUG) Log.d(TAG, "exposeFallback()");
|
||||
hide(); // Expose fallback
|
||||
}
|
||||
|
||||
// Allows the Face Unlock service to poke the wake lock to keep the lockscreen alive
|
||||
/**
|
||||
* Allows the Face Unlock service to poke the wake lock to keep the lockscreen alive
|
||||
*/
|
||||
@Override
|
||||
public void pokeWakelock() {
|
||||
if (DEBUG) Log.d(TAG, "FaceLock pokeWakelock()");
|
||||
if (DEBUG) Log.d(TAG, "pokeWakelock()");
|
||||
mKeyguardScreenCallback.pokeWakelock();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -303,8 +303,10 @@ class KeyguardStatusViewManager implements OnClickListener {
|
||||
mUpdateMonitor.registerInfoCallback(mInfoCallback);
|
||||
mUpdateMonitor.registerSimStateCallback(mSimStateCallback);
|
||||
resetStatusInfo();
|
||||
//Issue the faceunlock failure message in a centralized place
|
||||
if (mUpdateMonitor.getMaxFaceUnlockAttemptsReached()) {
|
||||
// Issue the biometric unlock failure message in a centralized place
|
||||
// TODO: we either need to make the Face Unlock multiple failures string a more general
|
||||
// 'biometric unlock' or have each biometric unlock handle this on their own.
|
||||
if (mUpdateMonitor.getMaxBiometricUnlockAttemptsReached()) {
|
||||
setInstructionText(getContext().getString(R.string.faceunlock_multiple_failures));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +82,8 @@ public class KeyguardUpdateMonitor {
|
||||
private CharSequence mTelephonySpn;
|
||||
|
||||
private int mFailedAttempts = 0;
|
||||
private int mFailedFaceUnlockAttempts = 0;
|
||||
private static final int FAILED_FACE_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 15;
|
||||
private int mFailedBiometricUnlockAttempts = 0;
|
||||
private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 15;
|
||||
|
||||
private boolean mClockVisible;
|
||||
|
||||
@@ -703,7 +703,7 @@ public class KeyguardUpdateMonitor {
|
||||
|
||||
public void clearFailedAttempts() {
|
||||
mFailedAttempts = 0;
|
||||
mFailedFaceUnlockAttempts = 0;
|
||||
mFailedBiometricUnlockAttempts = 0;
|
||||
}
|
||||
|
||||
public void reportFailedAttempt() {
|
||||
@@ -718,12 +718,12 @@ public class KeyguardUpdateMonitor {
|
||||
return mPhoneState;
|
||||
}
|
||||
|
||||
public void reportFailedFaceUnlockAttempt() {
|
||||
mFailedFaceUnlockAttempts++;
|
||||
public void reportFailedBiometricUnlockAttempt() {
|
||||
mFailedBiometricUnlockAttempts++;
|
||||
}
|
||||
|
||||
public boolean getMaxFaceUnlockAttemptsReached() {
|
||||
return mFailedFaceUnlockAttempts >= FAILED_FACE_UNLOCK_ATTEMPTS_BEFORE_BACKUP;
|
||||
public boolean getMaxBiometricUnlockAttemptsReached() {
|
||||
return mFailedBiometricUnlockAttempts >= FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP;
|
||||
}
|
||||
|
||||
public boolean isSimLocked() {
|
||||
|
||||
@@ -580,7 +580,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
restoreWidgetState();
|
||||
|
||||
if (mBiometricUnlock != null && startBiometricUnlock) {
|
||||
mBiometricUnlock.start(mSuppressBiometricUnlock);
|
||||
maybeStartBiometricUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,7 +623,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
} else {
|
||||
mHasDialog = false;
|
||||
if (mBiometricUnlock != null && startBiometricUnlock) {
|
||||
mBiometricUnlock.start(mSuppressBiometricUnlock);
|
||||
maybeStartBiometricUnlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -855,7 +855,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
}
|
||||
recreateUnlockScreen(unlockMode);
|
||||
if (mBiometricUnlock != null && restartBiometricUnlock) {
|
||||
mBiometricUnlock.start(mSuppressBiometricUnlock);
|
||||
maybeStartBiometricUnlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -971,7 +971,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
initializeTransportControlView(unlockView);
|
||||
|
||||
if (mBiometricUnlock != null) {
|
||||
mBiometricUnlock.initializeAreaView(unlockView);
|
||||
// TODO: make faceLockAreaView a more general biometricUnlockView
|
||||
// We will need to add our Face Unlock specific child views programmatically in
|
||||
// initializeView rather than having them in the XML files.
|
||||
mBiometricUnlock.initializeView(unlockView.findViewById(R.id.faceLockAreaView));
|
||||
}
|
||||
|
||||
mUnlockScreenMode = unlockMode;
|
||||
@@ -1154,4 +1157,24 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
return mBitmap.getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the biometric unlock if it should be started based on a number of factors including
|
||||
* the mSuppressBiometricUnlock flag. If it should not be started, it hides the biometric
|
||||
* unlock area.
|
||||
*/
|
||||
private void maybeStartBiometricUnlock() {
|
||||
if (mBiometricUnlock != null) {
|
||||
final boolean backupIsTimedOut = (mUpdateMonitor.getFailedAttempts() >=
|
||||
LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
|
||||
if (!mSuppressBiometricUnlock
|
||||
&& mUpdateMonitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
|
||||
&& !mUpdateMonitor.getMaxBiometricUnlockAttemptsReached()
|
||||
&& !backupIsTimedOut) {
|
||||
mBiometricUnlock.start();
|
||||
} else {
|
||||
mBiometricUnlock.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user