Loading policy/src/com/android/internal/policy/impl/BiometricSensorUnlock.java 0 → 100644 +52 −0 Original line number Diff line number Diff line /* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.policy.impl; import android.view.View; interface BiometricSensorUnlock { // Returns 'true' if the biometric sensor is available and is selected by user. public boolean installedAndSelected(); // Returns 'true' if the biometric sensor has started its unlock procedure but has not yet // accepted or rejected the user. 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. public void show(long timeoutMilliseconds); // Hide the interface, if any, exposing the lockscreen. 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. 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. public void cleanUp(); // Returns the Device Policy Manager quality (e.g. PASSWORD_QUALITY_BIOMETRIC_WEAK). public int getQuality(); } policy/src/com/android/internal/policy/impl/FaceUnlock.java +89 −92 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import com.android.internal.policy.IFaceLockCallback; import com.android.internal.policy.IFaceLockInterface; import com.android.internal.widget.LockPatternUtils; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading @@ -33,7 +34,7 @@ import android.telephony.TelephonyManager; import android.util.Log; import android.view.View; public class FaceUnlock implements Handler.Callback { public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { private static final boolean DEBUG = false; private static final String TAG = "FULLockscreen"; Loading @@ -52,10 +53,6 @@ public class FaceUnlock implements Handler.Callback { private boolean mServiceRunning = false; private final Object mServiceRunningLock = new Object(); // Long enough to stay visible while dialer comes up // Short enough to not be visible if the user goes back immediately private final int VIEW_AREA_EMERGENCY_DIALER_TIMEOUT = 1000; // 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 Loading @@ -80,22 +77,65 @@ public class FaceUnlock implements Handler.Callback { mHandler = new Handler(this); } public void cleanUp() { // Indicates whether FaceLock is in use public boolean installedAndSelected() { return (mLockPatternUtils.usingBiometricWeak() && mLockPatternUtils.isBiometricWeakInstalled()); } public boolean isRunning() { return mServiceRunning; } // Shows the FaceLock area for a period of time public void show(long timeoutMillis) { showArea(); if (timeoutMillis > 0) mHandler.sendEmptyMessageDelayed(MSG_HIDE_AREA_VIEW, timeoutMillis); } // Hides the FaceLock area immediately 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 public boolean stop() { boolean wasRunning = false; if (installedAndSelected()) { stopUi(); if (mBoundToService) { wasRunning = true; if (DEBUG) Log.d(TAG, "before unbind from FaceLock service"); if (mService != null) { try { mService.unregisterCallback(mFaceLockCallback); } catch (RemoteException e) { // Not much we can do } stop(); mService = null; } mContext.unbindService(mConnection); if (DEBUG) Log.d(TAG, "after unbind from FaceLock 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"); } } return wasRunning; } /** When screen is turned on and focused, need to bind to FaceLock service if we are using /** * 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 */ public void activateIfAble(boolean hasOverlay) { public boolean start(boolean suppressBiometricUnlock) { final boolean tooManyFaceUnlockTries = mUpdateMonitor.getMaxFaceUnlockAttemptsReached(); final int failedBackupAttempts = mUpdateMonitor.getFailedAttempts(); final boolean backupIsTimedOut = Loading @@ -103,42 +143,31 @@ public class FaceUnlock implements Handler.Callback { if (tooManyFaceUnlockTries) Log.i(TAG, "tooManyFaceUnlockTries: " + tooManyFaceUnlockTries); if (mUpdateMonitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE && installedAndSelected() && !hasOverlay && !suppressBiometricUnlock && !tooManyFaceUnlockTries && !backupIsTimedOut) { bind(); // Show FaceLock area, but only for a little bit so lockpattern will become visible if // FaceLock fails to start or crashes showAreaWithTimeout(VIEW_AREA_SERVICE_TIMEOUT); show(VIEW_AREA_SERVICE_TIMEOUT); // 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 { hideArea(); } } public boolean isServiceRunning() { return mServiceRunning; } public int viewAreaEmergencyDialerTimeout() { return VIEW_AREA_EMERGENCY_DIALER_TIMEOUT; hide(); return false; } // Indicates whether FaceLock is in use public boolean installedAndSelected() { return (mLockPatternUtils.usingBiometricWeak() && mLockPatternUtils.isBiometricWeakInstalled()); return true; } // Takes care of FaceLock area when layout is created public void initializeAreaView(View view) { public void initializeAreaView(View topView) { if (installedAndSelected()) { mAreaView = view.findViewById(R.id.faceLockAreaView); mAreaView = topView.findViewById(R.id.faceLockAreaView); if (mAreaView == null) { Log.e(TAG, "Layout does not have areaView and FaceLock is enabled"); } Loading @@ -147,13 +176,20 @@ public class FaceUnlock implements Handler.Callback { } } // Stops FaceLock if it is running and reports back whether it was running or not public boolean stopIfRunning() { if (installedAndSelected() && mBoundToService) { stopAndUnbind(); return true; public void cleanUp() { if (mService != null) { try { mService.unregisterCallback(mFaceLockCallback); } catch (RemoteException e) { // Not much we can do } return false; stopUi(); mService = null; } } public int getQuality() { return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; } // Handles covering or exposing FaceLock area on the client side when FaceLock starts or stops Loading Loading @@ -186,28 +222,15 @@ public class FaceUnlock implements Handler.Callback { } // Shows the FaceLock area immediately public void showArea() { private void showArea() { // Remove messages to prevent a delayed hide message from undo-ing the show removeAreaDisplayMessages(); mHandler.sendEmptyMessage(MSG_SHOW_AREA_VIEW); } // Hides the FaceLock area immediately public void hideArea() { // Remove messages to prevent a delayed show message from undo-ing the hide removeAreaDisplayMessages(); mHandler.sendEmptyMessage(MSG_HIDE_AREA_VIEW); } // Shows the FaceLock area for a period of time public void showAreaWithTimeout(long timeoutMillis) { showArea(); mHandler.sendEmptyMessageDelayed(MSG_HIDE_AREA_VIEW, timeoutMillis); } // 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. public void bind() { private void bind() { if (installedAndSelected()) { if (!mBoundToService) { if (DEBUG) Log.d(TAG, "before bind to FaceLock service"); Loading @@ -223,32 +246,6 @@ public class FaceUnlock implements Handler.Callback { } } // Tells FaceLock to stop and then unbinds from the FaceLock service public void stopAndUnbind() { if (installedAndSelected()) { stop(); if (mBoundToService) { if (DEBUG) Log.d(TAG, "before unbind from FaceLock service"); if (mService != null) { try { mService.unregisterCallback(mFaceLockCallback); } catch (RemoteException e) { // Not much we can do } } mContext.unbindService(mConnection); if (DEBUG) Log.d(TAG, "after unbind from FaceLock 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"); } } } private ServiceConnection mConnection = new ServiceConnection() { // Completes connection, registers callback and starts FaceLock when service is bound @Override Loading @@ -268,7 +265,7 @@ public class FaceUnlock implements Handler.Callback { int[] position; position = new int[2]; mAreaView.getLocationInWindow(position); start(mAreaView.getWindowToken(), position[0], position[1], startUi(mAreaView.getWindowToken(), position[0], position[1], mAreaView.getWidth(), mAreaView.getHeight()); } } Loading @@ -286,7 +283,7 @@ public class FaceUnlock implements Handler.Callback { }; // Tells the FaceLock service to start displaying its UI and perform recognition public void start(IBinder windowToken, int x, int y, int w, int h) { private void startUi(IBinder windowToken, int x, int y, int w, int h) { if (installedAndSelected()) { synchronized (mServiceRunningLock) { if (!mServiceRunning) { Loading @@ -300,14 +297,14 @@ public class FaceUnlock implements Handler.Callback { } mServiceRunning = true; } else { if (DEBUG) Log.w(TAG, "start() attempted while running"); if (DEBUG) Log.w(TAG, "startUi() attempted while running"); } } } } // Tells the FaceLock service to stop displaying its UI and stop recognition public void stop() { private void stopUi() { if (installedAndSelected()) { // 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 Loading @@ -333,7 +330,7 @@ public class FaceUnlock implements Handler.Callback { public void unlock() { if (DEBUG) Log.d(TAG, "FaceLock unlock()"); showArea(); // Keep fallback covered stopAndUnbind(); stop(); mKeyguardScreenCallback.keyguardDone(true); mKeyguardScreenCallback.reportSuccessfulUnlockAttempt(); Loading @@ -344,8 +341,8 @@ public class FaceUnlock implements Handler.Callback { @Override public void cancel() { if (DEBUG) Log.d(TAG, "FaceLock cancel()"); hideArea(); // Expose fallback stopAndUnbind(); hide(); // Expose fallback stop(); mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT); } Loading @@ -355,8 +352,8 @@ public class FaceUnlock implements Handler.Callback { public void reportFailedAttempt() { if (DEBUG) Log.d(TAG, "FaceLock reportFailedAttempt()"); mUpdateMonitor.reportFailedFaceUnlockAttempt(); hideArea(); // Expose fallback stopAndUnbind(); hide(); // Expose fallback stop(); mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT); } Loading @@ -364,7 +361,7 @@ public class FaceUnlock implements Handler.Callback { @Override public void exposeFallback() { if (DEBUG) Log.d(TAG, "FaceLock exposeFallback()"); hideArea(); // Expose fallback hide(); // Expose fallback } // Allows the Face Unlock service to poke the wake lock to keep the lockscreen alive Loading policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +65 −58 Original line number Diff line number Diff line Loading @@ -101,15 +101,17 @@ public class LockPatternKeyguardView extends KeyguardViewBase { private boolean mShowLockBeforeUnlock = false; // The following were added to support FaceLock private FaceUnlock mFaceUnlock; private final Object mFaceLockStartupLock = new Object(); // Interface to a biometric sensor that can optionally be used to unlock the device private BiometricSensorUnlock mBiometricUnlock; private final Object mBiometricUnlockStartupLock = new Object(); // Long enough to stay visible while dialer comes up // Short enough to not be visible if the user goes back immediately private final int BIOMETRIC_AREA_EMERGENCY_DIALER_TIMEOUT = 1000; private boolean mRequiresSim; //True if we have some sort of overlay on top of the Lockscreen //Also true if we've activated a phone call, either emergency dialing or incoming //This resets when the phone is turned off with no current call private boolean mHasOverlay; // True if the biometric unlock should not be displayed. For example, if there is an overlay on // lockscreen or the user is plugging in / unplugging the device. private boolean mSupressBiometricUnlock; //True if a dialog is currently displaying on top of this window //Unlike other overlays, this does not close with a power button cycle private boolean mHasDialog = false; Loading Loading @@ -308,15 +310,15 @@ public class LockPatternKeyguardView extends KeyguardViewBase { } public void takeEmergencyCallAction() { mHasOverlay = true; mSupressBiometricUnlock = true; // Continue showing FaceLock area until dialer comes up or call is resumed if (mFaceUnlock.installedAndSelected() && mFaceUnlock.isServiceRunning()) { mFaceUnlock.showAreaWithTimeout(mFaceUnlock.viewAreaEmergencyDialerTimeout()); if (mBiometricUnlock.installedAndSelected() && mBiometricUnlock.isRunning()) { // Continue covering backup lock until dialer comes up or call is resumed mBiometricUnlock.show(BIOMETRIC_AREA_EMERGENCY_DIALER_TIMEOUT); } // FaceLock must be stopped if it is running when emergency call is pressed mFaceUnlock.stopAndUnbind(); // The biometric unlock must be stopped if it is running when emergency call is pressed mBiometricUnlock.stop(); pokeWakelock(EMERGENCY_CALL_TIMEOUT); if (TelephonyManager.getDefault().getCallState() Loading Loading @@ -421,7 +423,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { LockPatternUtils lockPatternUtils, KeyguardWindowController controller) { super(context, callback); mFaceUnlock = new FaceUnlock(context, updateMonitor, lockPatternUtils, mBiometricUnlock = new FaceUnlock(context, updateMonitor, lockPatternUtils, mKeyguardScreenCallback); mConfiguration = context.getResources().getConfiguration(); mEnableFallback = false; Loading @@ -429,7 +431,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { mUpdateMonitor = updateMonitor; mLockPatternUtils = lockPatternUtils; mWindowController = controller; mHasOverlay = false; mSupressBiometricUnlock = false; mPluggedIn = mUpdateMonitor.isDevicePluggedIn(); mScreenOn = ((PowerManager)context.getSystemService(Context.POWER_SERVICE)).isScreenOn(); Loading Loading @@ -528,8 +530,8 @@ public class LockPatternKeyguardView extends KeyguardViewBase { if (DEBUG) Log.d(TAG, "screen off"); mScreenOn = false; mForgotPattern = false; mHasOverlay = mUpdateMonitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE || mHasDialog; mSupressBiometricUnlock = mUpdateMonitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE || mHasDialog; // Emulate activity life-cycle for both lock and unlock screen. if (mLockScreen != null) { Loading @@ -541,25 +543,25 @@ public class LockPatternKeyguardView extends KeyguardViewBase { saveWidgetState(); // When screen is turned off, need to unbind from FaceLock service if using FaceLock mFaceUnlock.stopAndUnbind(); // The biometric unlock must stop when screen turns off. mBiometricUnlock.stop(); } @Override public void onScreenTurnedOn() { if (DEBUG) Log.d(TAG, "screen on"); boolean runFaceLock = false; //Make sure to start facelock iff the screen is both on and focused synchronized(mFaceLockStartupLock) { boolean startBiometricUnlock = false; // Start the biometric unlock if and only if the screen is both on and focused synchronized(mBiometricUnlockStartupLock) { mScreenOn = true; runFaceLock = mWindowFocused; startBiometricUnlock = mWindowFocused; } show(); restoreWidgetState(); if (runFaceLock) mFaceUnlock.activateIfAble(mHasOverlay); if (startBiometricUnlock) mBiometricUnlock.start(mSupressBiometricUnlock); } private void saveWidgetState() { Loading @@ -578,25 +580,26 @@ public class LockPatternKeyguardView extends KeyguardViewBase { } } /** Unbind from facelock if something covers this window (such as an alarm) * bind to facelock if the lockscreen window just came into focus, and the screen is on /** * Stop the biometric unlock if something covers this window (such as an alarm) * Start the biometric unlock if the lockscreen window just came into focus and the screen is on */ @Override public void onWindowFocusChanged (boolean hasWindowFocus) { if (DEBUG) Log.d(TAG, hasWindowFocus ? "focused" : "unfocused"); boolean runFaceLock = false; //Make sure to start facelock iff the screen is both on and focused synchronized(mFaceLockStartupLock) { if(mScreenOn && !mWindowFocused) runFaceLock = hasWindowFocus; boolean startBiometricUnlock = false; // Start the biometric unlock if and only if the screen is both on and focused synchronized(mBiometricUnlockStartupLock) { if (mScreenOn && !mWindowFocused) startBiometricUnlock = hasWindowFocus; mWindowFocused = hasWindowFocus; } if (!hasWindowFocus) { mHasOverlay = true; mFaceUnlock.stopAndUnbind(); mFaceUnlock.hideArea(); mSupressBiometricUnlock = true; mBiometricUnlock.stop(); mBiometricUnlock.hide(); } else { mHasDialog = false; if (runFaceLock) mFaceUnlock.activateIfAble(mHasOverlay); if (startBiometricUnlock) mBiometricUnlock.start(mSupressBiometricUnlock); } } Loading @@ -610,14 +613,14 @@ public class LockPatternKeyguardView extends KeyguardViewBase { ((KeyguardScreen) mUnlockScreen).onResume(); } if (mFaceUnlock.installedAndSelected() && !mHasOverlay) { if (mBiometricUnlock.installedAndSelected() && !mSupressBiometricUnlock) { // Note that show() gets called before the screen turns off to set it up for next time // it is turned on. We don't want to set a timeout on the FaceLock area here because it // may be gone by the time the screen is turned on again. We set the timeout when the // screen turns on instead. mFaceUnlock.showArea(); // it is turned on. We don't want to set a timeout on the biometric unlock here because // it may be gone by the time the screen is turned on again. We set the timeout when // the screen turns on instead. mBiometricUnlock.show(0); } else { mFaceUnlock.hideArea(); mBiometricUnlock.hide(); } } Loading Loading @@ -651,9 +654,9 @@ public class LockPatternKeyguardView extends KeyguardViewBase { removeCallbacks(mRecreateRunnable); // When view is hidden, need to unbind from FaceLock service if we are using FaceLock // When view is hidden, we need to stop the biometric unlock // e.g., when device becomes unlocked mFaceUnlock.stopAndUnbind(); mBiometricUnlock.stop(); super.onDetachedFromWindow(); } Loading @@ -670,16 +673,19 @@ public class LockPatternKeyguardView extends KeyguardViewBase { InfoCallbackImpl mInfoCallback = new InfoCallbackImpl() { /** When somebody plugs in or unplugs the device, we don't want to display faceunlock */ /** * When somebody plugs in or unplugs the device, we don't want to display the biometric * unlock. */ @Override public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) { mHasOverlay |= mPluggedIn != pluggedIn; mSupressBiometricUnlock |= mPluggedIn != pluggedIn; mPluggedIn = pluggedIn; // If it's already running, don't close it down: the unplug didn't start it if (!mFaceUnlock.isServiceRunning()) { mFaceUnlock.stopAndUnbind(); mFaceUnlock.hideArea(); if (!mBiometricUnlock.isRunning()) { mBiometricUnlock.stop(); mBiometricUnlock.hide(); } } Loading @@ -690,20 +696,20 @@ public class LockPatternKeyguardView extends KeyguardViewBase { | (mUpdateMonitor.isClockVisible() ? View.STATUS_BAR_DISABLE_CLOCK : 0)); } //We need to stop faceunlock when a phonecall comes in // We need to stop the biometric unlock when a phone call comes in @Override public void onPhoneStateChanged(int phoneState) { if (DEBUG) Log.d(TAG, "phone state: " + phoneState); if(phoneState == TelephonyManager.CALL_STATE_RINGING) { mHasOverlay = true; mFaceUnlock.stopAndUnbind(); mFaceUnlock.hideArea(); mSupressBiometricUnlock = true; mBiometricUnlock.stop(); mBiometricUnlock.hide(); } } @Override public void onUserChanged(int userId) { mFaceUnlock.stopAndUnbind(); mBiometricUnlock.stop(); mLockPatternUtils.setCurrentUser(userId); updateScreen(getInitialMode(), true); } Loading Loading @@ -766,7 +772,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { mUnlockScreen = null; } mUpdateMonitor.removeCallback(this); mFaceUnlock.cleanUp(); mBiometricUnlock.cleanUp(); } private boolean isSecure() { Loading Loading @@ -816,10 +822,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase { final UnlockMode unlockMode = getUnlockMode(); if (mode == Mode.UnlockScreen && unlockMode != UnlockMode.Unknown) { if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) { boolean restartFaceLock = mFaceUnlock.stopIfRunning(); boolean restartBiometricUnlock = mBiometricUnlock.stop(); recreateUnlockScreen(unlockMode); if (restartFaceLock) { mFaceUnlock.activateIfAble(mHasOverlay); if (restartBiometricUnlock) { mBiometricUnlock.start(mSupressBiometricUnlock); } } } Loading Loading @@ -933,7 +939,8 @@ public class LockPatternKeyguardView extends KeyguardViewBase { throw new IllegalArgumentException("unknown unlock mode " + unlockMode); } initializeTransportControlView(unlockView); mFaceUnlock.initializeAreaView(unlockView); // Only shows view if FaceLock is enabled // Only shows view if the biometric unlock is enabled mBiometricUnlock.initializeAreaView(unlockView); mUnlockScreenMode = unlockMode; return unlockView; Loading Loading
policy/src/com/android/internal/policy/impl/BiometricSensorUnlock.java 0 → 100644 +52 −0 Original line number Diff line number Diff line /* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.policy.impl; import android.view.View; interface BiometricSensorUnlock { // Returns 'true' if the biometric sensor is available and is selected by user. public boolean installedAndSelected(); // Returns 'true' if the biometric sensor has started its unlock procedure but has not yet // accepted or rejected the user. 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. public void show(long timeoutMilliseconds); // Hide the interface, if any, exposing the lockscreen. 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. 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. public void cleanUp(); // Returns the Device Policy Manager quality (e.g. PASSWORD_QUALITY_BIOMETRIC_WEAK). public int getQuality(); }
policy/src/com/android/internal/policy/impl/FaceUnlock.java +89 −92 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import com.android.internal.policy.IFaceLockCallback; import com.android.internal.policy.IFaceLockInterface; import com.android.internal.widget.LockPatternUtils; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading @@ -33,7 +34,7 @@ import android.telephony.TelephonyManager; import android.util.Log; import android.view.View; public class FaceUnlock implements Handler.Callback { public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { private static final boolean DEBUG = false; private static final String TAG = "FULLockscreen"; Loading @@ -52,10 +53,6 @@ public class FaceUnlock implements Handler.Callback { private boolean mServiceRunning = false; private final Object mServiceRunningLock = new Object(); // Long enough to stay visible while dialer comes up // Short enough to not be visible if the user goes back immediately private final int VIEW_AREA_EMERGENCY_DIALER_TIMEOUT = 1000; // 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 Loading @@ -80,22 +77,65 @@ public class FaceUnlock implements Handler.Callback { mHandler = new Handler(this); } public void cleanUp() { // Indicates whether FaceLock is in use public boolean installedAndSelected() { return (mLockPatternUtils.usingBiometricWeak() && mLockPatternUtils.isBiometricWeakInstalled()); } public boolean isRunning() { return mServiceRunning; } // Shows the FaceLock area for a period of time public void show(long timeoutMillis) { showArea(); if (timeoutMillis > 0) mHandler.sendEmptyMessageDelayed(MSG_HIDE_AREA_VIEW, timeoutMillis); } // Hides the FaceLock area immediately 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 public boolean stop() { boolean wasRunning = false; if (installedAndSelected()) { stopUi(); if (mBoundToService) { wasRunning = true; if (DEBUG) Log.d(TAG, "before unbind from FaceLock service"); if (mService != null) { try { mService.unregisterCallback(mFaceLockCallback); } catch (RemoteException e) { // Not much we can do } stop(); mService = null; } mContext.unbindService(mConnection); if (DEBUG) Log.d(TAG, "after unbind from FaceLock 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"); } } return wasRunning; } /** When screen is turned on and focused, need to bind to FaceLock service if we are using /** * 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 */ public void activateIfAble(boolean hasOverlay) { public boolean start(boolean suppressBiometricUnlock) { final boolean tooManyFaceUnlockTries = mUpdateMonitor.getMaxFaceUnlockAttemptsReached(); final int failedBackupAttempts = mUpdateMonitor.getFailedAttempts(); final boolean backupIsTimedOut = Loading @@ -103,42 +143,31 @@ public class FaceUnlock implements Handler.Callback { if (tooManyFaceUnlockTries) Log.i(TAG, "tooManyFaceUnlockTries: " + tooManyFaceUnlockTries); if (mUpdateMonitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE && installedAndSelected() && !hasOverlay && !suppressBiometricUnlock && !tooManyFaceUnlockTries && !backupIsTimedOut) { bind(); // Show FaceLock area, but only for a little bit so lockpattern will become visible if // FaceLock fails to start or crashes showAreaWithTimeout(VIEW_AREA_SERVICE_TIMEOUT); show(VIEW_AREA_SERVICE_TIMEOUT); // 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 { hideArea(); } } public boolean isServiceRunning() { return mServiceRunning; } public int viewAreaEmergencyDialerTimeout() { return VIEW_AREA_EMERGENCY_DIALER_TIMEOUT; hide(); return false; } // Indicates whether FaceLock is in use public boolean installedAndSelected() { return (mLockPatternUtils.usingBiometricWeak() && mLockPatternUtils.isBiometricWeakInstalled()); return true; } // Takes care of FaceLock area when layout is created public void initializeAreaView(View view) { public void initializeAreaView(View topView) { if (installedAndSelected()) { mAreaView = view.findViewById(R.id.faceLockAreaView); mAreaView = topView.findViewById(R.id.faceLockAreaView); if (mAreaView == null) { Log.e(TAG, "Layout does not have areaView and FaceLock is enabled"); } Loading @@ -147,13 +176,20 @@ public class FaceUnlock implements Handler.Callback { } } // Stops FaceLock if it is running and reports back whether it was running or not public boolean stopIfRunning() { if (installedAndSelected() && mBoundToService) { stopAndUnbind(); return true; public void cleanUp() { if (mService != null) { try { mService.unregisterCallback(mFaceLockCallback); } catch (RemoteException e) { // Not much we can do } return false; stopUi(); mService = null; } } public int getQuality() { return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; } // Handles covering or exposing FaceLock area on the client side when FaceLock starts or stops Loading Loading @@ -186,28 +222,15 @@ public class FaceUnlock implements Handler.Callback { } // Shows the FaceLock area immediately public void showArea() { private void showArea() { // Remove messages to prevent a delayed hide message from undo-ing the show removeAreaDisplayMessages(); mHandler.sendEmptyMessage(MSG_SHOW_AREA_VIEW); } // Hides the FaceLock area immediately public void hideArea() { // Remove messages to prevent a delayed show message from undo-ing the hide removeAreaDisplayMessages(); mHandler.sendEmptyMessage(MSG_HIDE_AREA_VIEW); } // Shows the FaceLock area for a period of time public void showAreaWithTimeout(long timeoutMillis) { showArea(); mHandler.sendEmptyMessageDelayed(MSG_HIDE_AREA_VIEW, timeoutMillis); } // 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. public void bind() { private void bind() { if (installedAndSelected()) { if (!mBoundToService) { if (DEBUG) Log.d(TAG, "before bind to FaceLock service"); Loading @@ -223,32 +246,6 @@ public class FaceUnlock implements Handler.Callback { } } // Tells FaceLock to stop and then unbinds from the FaceLock service public void stopAndUnbind() { if (installedAndSelected()) { stop(); if (mBoundToService) { if (DEBUG) Log.d(TAG, "before unbind from FaceLock service"); if (mService != null) { try { mService.unregisterCallback(mFaceLockCallback); } catch (RemoteException e) { // Not much we can do } } mContext.unbindService(mConnection); if (DEBUG) Log.d(TAG, "after unbind from FaceLock 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"); } } } private ServiceConnection mConnection = new ServiceConnection() { // Completes connection, registers callback and starts FaceLock when service is bound @Override Loading @@ -268,7 +265,7 @@ public class FaceUnlock implements Handler.Callback { int[] position; position = new int[2]; mAreaView.getLocationInWindow(position); start(mAreaView.getWindowToken(), position[0], position[1], startUi(mAreaView.getWindowToken(), position[0], position[1], mAreaView.getWidth(), mAreaView.getHeight()); } } Loading @@ -286,7 +283,7 @@ public class FaceUnlock implements Handler.Callback { }; // Tells the FaceLock service to start displaying its UI and perform recognition public void start(IBinder windowToken, int x, int y, int w, int h) { private void startUi(IBinder windowToken, int x, int y, int w, int h) { if (installedAndSelected()) { synchronized (mServiceRunningLock) { if (!mServiceRunning) { Loading @@ -300,14 +297,14 @@ public class FaceUnlock implements Handler.Callback { } mServiceRunning = true; } else { if (DEBUG) Log.w(TAG, "start() attempted while running"); if (DEBUG) Log.w(TAG, "startUi() attempted while running"); } } } } // Tells the FaceLock service to stop displaying its UI and stop recognition public void stop() { private void stopUi() { if (installedAndSelected()) { // 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 Loading @@ -333,7 +330,7 @@ public class FaceUnlock implements Handler.Callback { public void unlock() { if (DEBUG) Log.d(TAG, "FaceLock unlock()"); showArea(); // Keep fallback covered stopAndUnbind(); stop(); mKeyguardScreenCallback.keyguardDone(true); mKeyguardScreenCallback.reportSuccessfulUnlockAttempt(); Loading @@ -344,8 +341,8 @@ public class FaceUnlock implements Handler.Callback { @Override public void cancel() { if (DEBUG) Log.d(TAG, "FaceLock cancel()"); hideArea(); // Expose fallback stopAndUnbind(); hide(); // Expose fallback stop(); mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT); } Loading @@ -355,8 +352,8 @@ public class FaceUnlock implements Handler.Callback { public void reportFailedAttempt() { if (DEBUG) Log.d(TAG, "FaceLock reportFailedAttempt()"); mUpdateMonitor.reportFailedFaceUnlockAttempt(); hideArea(); // Expose fallback stopAndUnbind(); hide(); // Expose fallback stop(); mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT); } Loading @@ -364,7 +361,7 @@ public class FaceUnlock implements Handler.Callback { @Override public void exposeFallback() { if (DEBUG) Log.d(TAG, "FaceLock exposeFallback()"); hideArea(); // Expose fallback hide(); // Expose fallback } // Allows the Face Unlock service to poke the wake lock to keep the lockscreen alive Loading
policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +65 −58 Original line number Diff line number Diff line Loading @@ -101,15 +101,17 @@ public class LockPatternKeyguardView extends KeyguardViewBase { private boolean mShowLockBeforeUnlock = false; // The following were added to support FaceLock private FaceUnlock mFaceUnlock; private final Object mFaceLockStartupLock = new Object(); // Interface to a biometric sensor that can optionally be used to unlock the device private BiometricSensorUnlock mBiometricUnlock; private final Object mBiometricUnlockStartupLock = new Object(); // Long enough to stay visible while dialer comes up // Short enough to not be visible if the user goes back immediately private final int BIOMETRIC_AREA_EMERGENCY_DIALER_TIMEOUT = 1000; private boolean mRequiresSim; //True if we have some sort of overlay on top of the Lockscreen //Also true if we've activated a phone call, either emergency dialing or incoming //This resets when the phone is turned off with no current call private boolean mHasOverlay; // True if the biometric unlock should not be displayed. For example, if there is an overlay on // lockscreen or the user is plugging in / unplugging the device. private boolean mSupressBiometricUnlock; //True if a dialog is currently displaying on top of this window //Unlike other overlays, this does not close with a power button cycle private boolean mHasDialog = false; Loading Loading @@ -308,15 +310,15 @@ public class LockPatternKeyguardView extends KeyguardViewBase { } public void takeEmergencyCallAction() { mHasOverlay = true; mSupressBiometricUnlock = true; // Continue showing FaceLock area until dialer comes up or call is resumed if (mFaceUnlock.installedAndSelected() && mFaceUnlock.isServiceRunning()) { mFaceUnlock.showAreaWithTimeout(mFaceUnlock.viewAreaEmergencyDialerTimeout()); if (mBiometricUnlock.installedAndSelected() && mBiometricUnlock.isRunning()) { // Continue covering backup lock until dialer comes up or call is resumed mBiometricUnlock.show(BIOMETRIC_AREA_EMERGENCY_DIALER_TIMEOUT); } // FaceLock must be stopped if it is running when emergency call is pressed mFaceUnlock.stopAndUnbind(); // The biometric unlock must be stopped if it is running when emergency call is pressed mBiometricUnlock.stop(); pokeWakelock(EMERGENCY_CALL_TIMEOUT); if (TelephonyManager.getDefault().getCallState() Loading Loading @@ -421,7 +423,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { LockPatternUtils lockPatternUtils, KeyguardWindowController controller) { super(context, callback); mFaceUnlock = new FaceUnlock(context, updateMonitor, lockPatternUtils, mBiometricUnlock = new FaceUnlock(context, updateMonitor, lockPatternUtils, mKeyguardScreenCallback); mConfiguration = context.getResources().getConfiguration(); mEnableFallback = false; Loading @@ -429,7 +431,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { mUpdateMonitor = updateMonitor; mLockPatternUtils = lockPatternUtils; mWindowController = controller; mHasOverlay = false; mSupressBiometricUnlock = false; mPluggedIn = mUpdateMonitor.isDevicePluggedIn(); mScreenOn = ((PowerManager)context.getSystemService(Context.POWER_SERVICE)).isScreenOn(); Loading Loading @@ -528,8 +530,8 @@ public class LockPatternKeyguardView extends KeyguardViewBase { if (DEBUG) Log.d(TAG, "screen off"); mScreenOn = false; mForgotPattern = false; mHasOverlay = mUpdateMonitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE || mHasDialog; mSupressBiometricUnlock = mUpdateMonitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE || mHasDialog; // Emulate activity life-cycle for both lock and unlock screen. if (mLockScreen != null) { Loading @@ -541,25 +543,25 @@ public class LockPatternKeyguardView extends KeyguardViewBase { saveWidgetState(); // When screen is turned off, need to unbind from FaceLock service if using FaceLock mFaceUnlock.stopAndUnbind(); // The biometric unlock must stop when screen turns off. mBiometricUnlock.stop(); } @Override public void onScreenTurnedOn() { if (DEBUG) Log.d(TAG, "screen on"); boolean runFaceLock = false; //Make sure to start facelock iff the screen is both on and focused synchronized(mFaceLockStartupLock) { boolean startBiometricUnlock = false; // Start the biometric unlock if and only if the screen is both on and focused synchronized(mBiometricUnlockStartupLock) { mScreenOn = true; runFaceLock = mWindowFocused; startBiometricUnlock = mWindowFocused; } show(); restoreWidgetState(); if (runFaceLock) mFaceUnlock.activateIfAble(mHasOverlay); if (startBiometricUnlock) mBiometricUnlock.start(mSupressBiometricUnlock); } private void saveWidgetState() { Loading @@ -578,25 +580,26 @@ public class LockPatternKeyguardView extends KeyguardViewBase { } } /** Unbind from facelock if something covers this window (such as an alarm) * bind to facelock if the lockscreen window just came into focus, and the screen is on /** * Stop the biometric unlock if something covers this window (such as an alarm) * Start the biometric unlock if the lockscreen window just came into focus and the screen is on */ @Override public void onWindowFocusChanged (boolean hasWindowFocus) { if (DEBUG) Log.d(TAG, hasWindowFocus ? "focused" : "unfocused"); boolean runFaceLock = false; //Make sure to start facelock iff the screen is both on and focused synchronized(mFaceLockStartupLock) { if(mScreenOn && !mWindowFocused) runFaceLock = hasWindowFocus; boolean startBiometricUnlock = false; // Start the biometric unlock if and only if the screen is both on and focused synchronized(mBiometricUnlockStartupLock) { if (mScreenOn && !mWindowFocused) startBiometricUnlock = hasWindowFocus; mWindowFocused = hasWindowFocus; } if (!hasWindowFocus) { mHasOverlay = true; mFaceUnlock.stopAndUnbind(); mFaceUnlock.hideArea(); mSupressBiometricUnlock = true; mBiometricUnlock.stop(); mBiometricUnlock.hide(); } else { mHasDialog = false; if (runFaceLock) mFaceUnlock.activateIfAble(mHasOverlay); if (startBiometricUnlock) mBiometricUnlock.start(mSupressBiometricUnlock); } } Loading @@ -610,14 +613,14 @@ public class LockPatternKeyguardView extends KeyguardViewBase { ((KeyguardScreen) mUnlockScreen).onResume(); } if (mFaceUnlock.installedAndSelected() && !mHasOverlay) { if (mBiometricUnlock.installedAndSelected() && !mSupressBiometricUnlock) { // Note that show() gets called before the screen turns off to set it up for next time // it is turned on. We don't want to set a timeout on the FaceLock area here because it // may be gone by the time the screen is turned on again. We set the timeout when the // screen turns on instead. mFaceUnlock.showArea(); // it is turned on. We don't want to set a timeout on the biometric unlock here because // it may be gone by the time the screen is turned on again. We set the timeout when // the screen turns on instead. mBiometricUnlock.show(0); } else { mFaceUnlock.hideArea(); mBiometricUnlock.hide(); } } Loading Loading @@ -651,9 +654,9 @@ public class LockPatternKeyguardView extends KeyguardViewBase { removeCallbacks(mRecreateRunnable); // When view is hidden, need to unbind from FaceLock service if we are using FaceLock // When view is hidden, we need to stop the biometric unlock // e.g., when device becomes unlocked mFaceUnlock.stopAndUnbind(); mBiometricUnlock.stop(); super.onDetachedFromWindow(); } Loading @@ -670,16 +673,19 @@ public class LockPatternKeyguardView extends KeyguardViewBase { InfoCallbackImpl mInfoCallback = new InfoCallbackImpl() { /** When somebody plugs in or unplugs the device, we don't want to display faceunlock */ /** * When somebody plugs in or unplugs the device, we don't want to display the biometric * unlock. */ @Override public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) { mHasOverlay |= mPluggedIn != pluggedIn; mSupressBiometricUnlock |= mPluggedIn != pluggedIn; mPluggedIn = pluggedIn; // If it's already running, don't close it down: the unplug didn't start it if (!mFaceUnlock.isServiceRunning()) { mFaceUnlock.stopAndUnbind(); mFaceUnlock.hideArea(); if (!mBiometricUnlock.isRunning()) { mBiometricUnlock.stop(); mBiometricUnlock.hide(); } } Loading @@ -690,20 +696,20 @@ public class LockPatternKeyguardView extends KeyguardViewBase { | (mUpdateMonitor.isClockVisible() ? View.STATUS_BAR_DISABLE_CLOCK : 0)); } //We need to stop faceunlock when a phonecall comes in // We need to stop the biometric unlock when a phone call comes in @Override public void onPhoneStateChanged(int phoneState) { if (DEBUG) Log.d(TAG, "phone state: " + phoneState); if(phoneState == TelephonyManager.CALL_STATE_RINGING) { mHasOverlay = true; mFaceUnlock.stopAndUnbind(); mFaceUnlock.hideArea(); mSupressBiometricUnlock = true; mBiometricUnlock.stop(); mBiometricUnlock.hide(); } } @Override public void onUserChanged(int userId) { mFaceUnlock.stopAndUnbind(); mBiometricUnlock.stop(); mLockPatternUtils.setCurrentUser(userId); updateScreen(getInitialMode(), true); } Loading Loading @@ -766,7 +772,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { mUnlockScreen = null; } mUpdateMonitor.removeCallback(this); mFaceUnlock.cleanUp(); mBiometricUnlock.cleanUp(); } private boolean isSecure() { Loading Loading @@ -816,10 +822,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase { final UnlockMode unlockMode = getUnlockMode(); if (mode == Mode.UnlockScreen && unlockMode != UnlockMode.Unknown) { if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) { boolean restartFaceLock = mFaceUnlock.stopIfRunning(); boolean restartBiometricUnlock = mBiometricUnlock.stop(); recreateUnlockScreen(unlockMode); if (restartFaceLock) { mFaceUnlock.activateIfAble(mHasOverlay); if (restartBiometricUnlock) { mBiometricUnlock.start(mSupressBiometricUnlock); } } } Loading Loading @@ -933,7 +939,8 @@ public class LockPatternKeyguardView extends KeyguardViewBase { throw new IllegalArgumentException("unknown unlock mode " + unlockMode); } initializeTransportControlView(unlockView); mFaceUnlock.initializeAreaView(unlockView); // Only shows view if FaceLock is enabled // Only shows view if the biometric unlock is enabled mBiometricUnlock.initializeAreaView(unlockView); mUnlockScreenMode = unlockMode; return unlockView; Loading