From 6edf2637e96139735df83907c221cce16d4d7eaa Mon Sep 17 00:00:00 2001 From: Jim Miller Date: Mon, 5 Sep 2011 16:03:14 -0700 Subject: [PATCH] Fix 5185505: Add support for weak biometric sensors to lockscreen. Added binder interfaces to the framework. Change-Id: I7d55b45baa4d1600ebd2a3828e85c3357cfcfb58 --- Android.mk | 2 + .../internal/policy/IFaceLockCallback.aidl | 25 +++ .../internal/policy/IFaceLockInterface.aidl | 26 +++ .../internal/widget/LockPatternUtils.java | 69 +++++++- .../policy/impl/KeyguardViewBase.java | 9 + .../policy/impl/KeyguardViewManager.java | 14 ++ .../policy/impl/LockPatternKeyguardView.java | 162 +++++++++++++++++- .../policy/impl/PatternUnlockScreen.java | 6 +- 8 files changed, 307 insertions(+), 6 deletions(-) create mode 100644 core/java/com/android/internal/policy/IFaceLockCallback.aidl create mode 100644 core/java/com/android/internal/policy/IFaceLockInterface.aidl diff --git a/Android.mk b/Android.mk index d4dc08847ae3e..352e5ebca4fc0 100644 --- a/Android.mk +++ b/Android.mk @@ -150,6 +150,8 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/appwidget/IAppWidgetService.aidl \ core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \ core/java/com/android/internal/backup/IBackupTransport.aidl \ + core/java/com/android/internal/policy/IFaceLockCallback.aidl \ + core/java/com/android/internal/policy/IFaceLockInterface.aidl \ core/java/com/android/internal/os/IDropBoxManagerService.aidl \ core/java/com/android/internal/os/IResultReceiver.aidl \ core/java/com/android/internal/statusbar/IStatusBar.aidl \ diff --git a/core/java/com/android/internal/policy/IFaceLockCallback.aidl b/core/java/com/android/internal/policy/IFaceLockCallback.aidl new file mode 100644 index 0000000000000..1eadc412810b6 --- /dev/null +++ b/core/java/com/android/internal/policy/IFaceLockCallback.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011 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; + +import android.os.IBinder; + +/** {@hide} */ +oneway interface IFaceLockCallback { + void unlock(); + void cancel(); + void sleepDevice(); +} diff --git a/core/java/com/android/internal/policy/IFaceLockInterface.aidl b/core/java/com/android/internal/policy/IFaceLockInterface.aidl new file mode 100644 index 0000000000000..921b8c7df88f7 --- /dev/null +++ b/core/java/com/android/internal/policy/IFaceLockInterface.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2011 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; + +import android.os.IBinder; +import com.android.internal.policy.IFaceLockCallback; + +/** {@hide} */ +interface IFaceLockInterface { + void startUi(IBinder containingWindowToken, int x, int y, int width, int height); + void stopUi(); + void registerCallback(IFaceLockCallback cb); +} diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 804f28a386074..4d828c448fe21 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -106,8 +106,11 @@ public class LockPatternUtils { private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; + public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate"; private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; private final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled"; + public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK + = "lockscreen.biometric_weak_fallback"; private final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory"; @@ -373,6 +376,7 @@ public class LockPatternUtils { setLockPatternEnabled(false); saveLockPattern(null); setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); } /** @@ -400,6 +404,15 @@ public class LockPatternUtils { * @param pattern The new pattern to save. */ public void saveLockPattern(List pattern) { + this.saveLockPattern(pattern, false); + } + + /** + * Save a lock pattern. + * @param pattern The new pattern to save. + * @param isFallback Specifies if this is a fallback to biometric weak + */ + public void saveLockPattern(List pattern, boolean isFallback) { // Compute the hash final byte[] hash = LockPatternUtils.patternToHash(pattern); try { @@ -417,7 +430,13 @@ public class LockPatternUtils { if (pattern != null) { keyStore.password(patternToString(pattern)); setBoolean(PATTERN_EVER_CHOSEN_KEY, true); - setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + if (!isFallback) { + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + } else { + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK); + setLong(PASSWORD_TYPE_ALTERNATE_KEY, + DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + } dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern .size(), 0, 0, 0, 0, 0, 0); } else { @@ -493,6 +512,18 @@ public class LockPatternUtils { * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} */ public void saveLockPassword(String password, int quality) { + this.saveLockPassword(password, quality, false); + } + + /** + * Save a lock password. Does not ensure that the password is as good + * as the requested mode, but will adjust the mode to be as good as the + * pattern. + * @param password The password to save + * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} + * @param isFallback Specifies if this is a fallback to biometric weak + */ + public void saveLockPassword(String password, int quality, boolean isFallback) { // Compute the hash final byte[] hash = passwordToHash(password); try { @@ -515,7 +546,12 @@ public class LockPatternUtils { keyStore.password(password); int computedQuality = computePasswordQuality(password); - setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality)); + if (!isFallback) { + setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality)); + } else { + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK); + setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality)); + } if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { int letters = 0; int uppercase = 0; @@ -590,7 +626,22 @@ public class LockPatternUtils { * @return stored password quality */ public int getKeyguardStoredPasswordQuality() { - return (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + int quality = + (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + // If the user has chosen to use weak biometric sensor, then return the backup locking + // method and treat biometric as a special case. + if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) { + quality = + (int) getLong(PASSWORD_TYPE_ALTERNATE_KEY, + DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + } + return quality; + } + + public boolean usingBiometricWeak() { + int quality = + (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; } /** @@ -725,6 +776,15 @@ public class LockPatternUtils { == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; } + /** + * @return Whether biometric weak lock is enabled. + */ + public boolean isBiometricEnabled() { + // TODO: check if it's installed + return getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) + == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; + } + /** * Set whether the lock pattern is enabled. */ @@ -863,7 +923,8 @@ public class LockPatternUtils { || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists() - || isPassword && savedPasswordExists(); + || isPassword && savedPasswordExists() + || usingBiometricWeak() && isBiometricEnabled(); return secure; } diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java index 74dde9c93f5d2..2fcf1dc3ebc2c 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java @@ -127,6 +127,15 @@ public abstract class KeyguardViewBase extends FrameLayout { */ abstract public void cleanUp(); + /** + * These were added to support FaceLock because the KeyguardViewManager needs to tell the + * LockPatternKeyguardView when to bind and and unbind with FaceLock service. Although + * implemented in LockPatternKeyguardView, these are not implemented in anything else + * derived from KeyguardViewBase + */ + abstract public void bindToFaceLock(); + abstract public void stopAndUnbindFromFaceLock(); + @Override public boolean dispatchKeyEvent(KeyEvent event) { if (shouldEventKeepScreenOnWhileKeyguardShowing(event)) { diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java index c1f1151bbcfda..9588a01707e11 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java @@ -31,6 +31,8 @@ import android.view.ViewManager; import android.view.WindowManager; import android.widget.FrameLayout; +import android.graphics.Color; + /** * Manages creating, showing, hiding and resetting the keyguard. Calls back * via {@link com.android.internal.policy.impl.KeyguardViewCallback} to poke @@ -160,6 +162,7 @@ public class KeyguardViewManager implements KeyguardWindowController { mKeyguardView.onScreenTurnedOn(); } } + mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams); mKeyguardHost.setVisibility(View.VISIBLE); mKeyguardView.requestFocus(); @@ -195,6 +198,9 @@ public class KeyguardViewManager implements KeyguardWindowController { if (mKeyguardView != null) { mKeyguardView.onScreenTurnedOff(); } + + // When screen is turned off, need to unbind from FaceLock service if we are using FaceLock + mKeyguardView.stopAndUnbindFromFaceLock(); } public synchronized void onScreenTurnedOn() { @@ -203,6 +209,9 @@ public class KeyguardViewManager implements KeyguardWindowController { if (mKeyguardView != null) { mKeyguardView.onScreenTurnedOn(); } + + // When screen is turned on, need to bind to FaceLock service if we are using FaceLock + mKeyguardView.bindToFaceLock(); } public synchronized void verifyUnlock() { @@ -238,6 +247,11 @@ public class KeyguardViewManager implements KeyguardWindowController { */ public synchronized void hide() { if (DEBUG) Log.d(TAG, "hide()"); + + // When view is hidden, need to unbind from FaceLock service if we are using FaceLock + // e.g., when device becomes unlocked + mKeyguardView.stopAndUnbindFromFaceLock(); + if (mKeyguardHost != null) { mKeyguardHost.setVisibility(View.GONE); // Don't do this right away, so we can let the view continue to animate diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java index 0de2de955c803..1d311d6589712 100644 --- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -18,6 +18,8 @@ package com.android.internal.policy.impl; import com.android.internal.R; import com.android.internal.policy.impl.LockPatternKeyguardView.UnlockMode; +import com.android.internal.policy.IFaceLockCallback; +import com.android.internal.policy.IFaceLockInterface; import com.android.internal.telephony.IccCard; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockScreenWidgetCallback; @@ -32,16 +34,20 @@ import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.AlertDialog; import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; +import android.content.ServiceConnection; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.telephony.TelephonyManager; @@ -56,6 +62,7 @@ import android.view.accessibility.AccessibilityManager; import java.io.IOException; + /** * The host view for all of the screens of the pattern unlock screen. There are * two {@link Mode}s of operation, lock and unlock. This will show the appropriate @@ -93,6 +100,12 @@ public class LockPatternKeyguardView extends KeyguardViewBase { private boolean mShowLockBeforeUnlock = false; + // The following were added to support FaceLock + private IFaceLockInterface mFaceLockService; + private boolean mBoundToFaceLockService = false; + private boolean mFaceLockServiceRunning = false; + private View mFaceLockAreaView; + /** * The current {@link KeyguardScreen} will use this to communicate back to us. */ @@ -580,6 +593,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase { } private boolean isSecure() { + // TODO: make this work with SIM and Account cases below. + boolean usingBiometric = mLockPatternUtils.usingBiometricWeak(); + if (usingBiometric && mLockPatternUtils.isBiometricEnabled()) + return true; UnlockMode unlockMode = getUnlockMode(); boolean secure = false; switch (unlockMode) { @@ -687,6 +704,12 @@ public class LockPatternKeyguardView extends KeyguardViewBase { mKeyguardScreenCallback, mUpdateMonitor.getFailedAttempts()); view.setEnableFallback(mEnableFallback); + + // TODO(bcolonna): For pattern unlock, it can give us the view where the pattern is + // displayed and FaceLock can draw in that area. + // For other views it's not so simple and we should probably change how the FaceLock + // area is determined. + mFaceLockAreaView = view.getUnlockAreaView(); unlockView = view; } else if (unlockMode == UnlockMode.SimPuk) { unlockView = new SimPukUnlockScreen( @@ -912,5 +935,142 @@ public class LockPatternKeyguardView extends KeyguardViewBase { return mBitmap.getHeight(); } } -} + // Everything below pertains to FaceLock - might want to separate this out + + // Binds to FaceLock service, but does not tell it to start + public void bindToFaceLock() { + if (mLockPatternUtils.usingBiometricWeak()) { + if (!mBoundToFaceLockService) { + if (DEBUG) Log.d(TAG, "before bind to FaceLock service"); + mContext.bindService(new Intent(IFaceLockInterface.class.getName()), + mFaceLockConnection, + Context.BIND_AUTO_CREATE); + if (DEBUG) Log.d(TAG, "after bind to FaceLock service"); + mBoundToFaceLockService = true; + } else { + // On startup I've seen onScreenTurnedOn() get called twice without + // onScreenTurnedOff() being called in between, which can cause this (bcolonna) + if (DEBUG) Log.w(TAG, "Attempt to bind to FaceLock when already bound"); + } + } + } + + // Tells FaceLock to stop and then unbinds from the FaceLock service + public void stopAndUnbindFromFaceLock() { + if (mLockPatternUtils.usingBiometricWeak()) { + stopFaceLock(); + + if (mBoundToFaceLockService) { + if (DEBUG) Log.d(TAG, "before unbind from FaceLock service"); + mContext.unbindService(mFaceLockConnection); + if (DEBUG) Log.d(TAG, "after unbind from FaceLock service"); + mBoundToFaceLockService = false; + } else { + // This could probably happen after the session when someone activates FaceLock + // because it wasn't active when the phone was turned on + if (DEBUG) Log.w(TAG, "Attempt to unbind from FaceLock when not bound"); + } + } + } + + private ServiceConnection mFaceLockConnection = new ServiceConnection() { + // Completes connection, registers callback and starts FaceLock when service is bound + @Override + public void onServiceConnected(ComponentName className, IBinder iservice) { + mFaceLockService = IFaceLockInterface.Stub.asInterface(iservice); + if (DEBUG) Log.d(TAG, "Connected to FaceLock service"); + try { + mFaceLockService.registerCallback(mFaceLockCallback); + } catch (RemoteException e) { + throw new RuntimeException("Remote exception"); + } + + // TODO(bcolonna): Need to set location properly (only works for pattern view now) + if (mFaceLockAreaView != null) { + int[] unlockLocationOnScreen = new int[2]; + mFaceLockAreaView.getLocationOnScreen(unlockLocationOnScreen); + int x = unlockLocationOnScreen[0]; + int y = unlockLocationOnScreen[1]; + int w = mFaceLockAreaView.getWidth(); + int h = mFaceLockAreaView.getHeight(); + if (DEBUG) Log.d(TAG, "(x,y) (wxh): (" + x + "," + y + ") (" + w + "x" + h + ")"); + startFaceLock(mFaceLockAreaView.getWindowToken(), x, y, w, h); + } + } + + // Cleans up if FaceLock service unexpectedly disconnects + @Override + public void onServiceDisconnected(ComponentName className) { + mFaceLockService = null; + if (DEBUG) Log.w(TAG, "Unexpected disconnect from FaceLock service"); + } + }; + + // Tells the FaceLock service to start displaying its UI and perform recognition + public void startFaceLock(IBinder windowToken, int x, int y, int h, int w) + { + if (mLockPatternUtils.usingBiometricWeak()) { + if (!mFaceLockServiceRunning) { + if (DEBUG) Log.d(TAG, "Starting FaceLock"); + try { + mFaceLockService.startUi(windowToken, x, y, h, w); + } catch (RemoteException e) { + throw new RuntimeException("Remote exception"); + } + mFaceLockServiceRunning = true; + } else { + if (DEBUG) Log.w(TAG, "startFaceLock() attempted while running"); + } + } + } + + // Tells the FaceLock service to stop displaying its UI and stop recognition + public void stopFaceLock() + { + if (mLockPatternUtils.usingBiometricWeak()) { + // 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 + // screen is turned off. That's why we check. + if (mFaceLockServiceRunning) { + try { + if (DEBUG) Log.d(TAG, "Stopping FaceLock"); + mFaceLockService.stopUi(); + } catch (RemoteException e) { + throw new RuntimeException("Remote exception"); + } + mFaceLockServiceRunning = 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 + @Override + public void unlock() { + if (DEBUG) Log.d(TAG, "FaceLock unlock"); + stopFaceLock(); + mKeyguardScreenCallback.keyguardDone(true); + mKeyguardScreenCallback.reportSuccessfulUnlockAttempt(); + } + + // Stops the FaceLock UI and exposes the backup method without unlocking + @Override + public void cancel() { + // In this case, either the user has cancelled out, or FaceLock failed to recognize them + if (DEBUG) Log.d(TAG, "FaceLock cancel"); + stopFaceLock(); + } + + // Stops the FaceLock UI and puts the phone to sleep + @Override + public void sleepDevice() { + // In this case, it appears the phone has been turned on accidentally + if (DEBUG) Log.d(TAG, "FaceLock accidental turn on"); + stopFaceLock(); + // TODO(bcolonna): how do we put the phone back to sleep (i.e., turn off the screen) + } + }; +} diff --git a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java index a3db1c30ff1e8..0cafeb5a147f4 100644 --- a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java @@ -199,7 +199,11 @@ class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient setFocusableInTouchMode(true); } - + // TODO(bcolonna): This is to tell FaceLock where to draw...but this covers up the wireless + // service text, so we will want to change the way the area is specified + public View getUnlockAreaView() { + return mLockPatternView; + } public void setEnableFallback(boolean state) { if (DEBUG) Log.d(TAG, "setEnableFallback(" + state + ")");