Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 460fa3c3 authored by Brian Colonna's avatar Brian Colonna Committed by Android Git Automerger
Browse files

am 6bc75916: Merge "Changes to biometric sensor interface in lockscreen" into jb-dev

* commit '6bc75916':
  Changes to biometric sensor interface in lockscreen
parents ded38270 6bc75916
Loading
Loading
Loading
Loading
+48 −18
Original line number Diff line number Diff line
@@ -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.
    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);
    /**
     * 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();

    // Provide a view to work within.
    public void initializeAreaView(View topView);
    /**
     * Stops the biometric unlock procedure and unbinds from the service.
     * @return whether the biometric unlock was running when called.
     */
    public boolean stop();

    // 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();
}
+156 −130
Original line number Diff line number Diff line
@@ -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);
    }

    /**
     * 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);
    }

    /**
     * Indicates whether Face Unlock is currently running.
     */
    public boolean isRunning() {
        return mServiceRunning;
        return mIsRunning;
    }

    // Shows the FaceLock area for a period of time
    // Called on the UI thread
    /**
     * 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();
        }
    };
+4 −2
Original line number Diff line number Diff line
@@ -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));
        }
    }
+7 −7
Original line number Diff line number Diff line
@@ -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() {
+27 −4

File changed.

Preview size limit exceeded, changes collapsed.