summaryrefslogtreecommitdiffstats
path: root/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
diff options
context:
space:
mode:
authorJim Miller <jaggies@google.com>2014-01-14 18:57:03 -0800
committerJim Miller <jaggies@google.com>2014-01-27 15:21:39 -0800
commit7751ff6cd079e59e3c1f2404198774cd371ea69f (patch)
tree30030a609b5152d9c42a03af1bcd4409add2ad01 /packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
parentdb7516e3bd24589525c42b9b4761381b547dc3be (diff)
downloadframeworks_base-7751ff6cd079e59e3c1f2404198774cd371ea69f.zip
frameworks_base-7751ff6cd079e59e3c1f2404198774cd371ea69f.tar.gz
frameworks_base-7751ff6cd079e59e3c1f2404198774cd371ea69f.tar.bz2
Move majority of security-related logic from KeyguardHostView
to KeyguardSecurityContainer. This removes and/or simplifies the interface between modules to allow easier separation of KeyguardSecurityContainer into a stand-alone component. Bug 12135931
Diffstat (limited to 'packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java')
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java483
1 files changed, 481 insertions, 2 deletions
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 9d03c6a..7606689 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -1,11 +1,39 @@
package com.android.keyguard;
+import android.app.Activity;
+import android.app.AlertDialog;
import android.content.Context;
import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.WindowManager;
import android.widget.FrameLayout;
-public class KeyguardSecurityContainer extends FrameLayout {
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+
+public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
+ private static final boolean DEBUG = KeyguardViewMediator.DEBUG;
+ private static final String TAG = "KeyguardSecurityView";
+ private KeyguardSecurityModel mSecurityModel;
+ private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
+ private LockPatternUtils mLockPatternUtils;
+
+ private KeyguardSecurityViewFlipper mSecurityViewFlipper;
+ private boolean mIsVerifyUnlockOnly;
+ private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
+ private boolean mIsBouncing;
+ private SecurityCallback mSecurityCallback;
+
+ // Used to notify the container when something interesting happens.
+ public interface SecurityCallback {
+ public void dismiss(boolean authenticated);
+ public void userActivity(long timeout);
+ public void finish();
+ }
+
public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@@ -16,9 +44,336 @@ public class KeyguardSecurityContainer extends FrameLayout {
public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ mSecurityModel = new KeyguardSecurityModel(context);
+ mLockPatternUtils = new LockPatternUtils(context);
+ }
+
+ public void setSecurityCallback(SecurityCallback callback) {
+ mSecurityCallback = callback;
+ }
+
+ @Override
+ public void onResume(int reason) {
+ getSecurityView(mCurrentSecuritySelection).onResume(reason);
+ }
+
+ @Override
+ public void onPause() {
+ getSecurityView(mCurrentSecuritySelection).onPause();
+ }
+
+ void updateSecurityViews(boolean isBouncing) {
+ int children = mSecurityViewFlipper.getChildCount();
+ for (int i = 0; i < children; i++) {
+ updateSecurityView(mSecurityViewFlipper.getChildAt(i), isBouncing);
+ }
+ }
+
+ public void announceCurrentSecurityMethod() {
+ View v = (View) getSecurityView(mCurrentSecuritySelection);
+ if (v != null) {
+ v.announceForAccessibility(v.getContentDescription());
+ }
+ }
+
+ private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
+ final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
+ KeyguardSecurityView view = null;
+ final int children = mSecurityViewFlipper.getChildCount();
+ for (int child = 0; child < children; child++) {
+ if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
+ view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
+ break;
+ }
+ }
+ int layoutId = getLayoutIdFor(securityMode);
+ if (view == null && layoutId != 0) {
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
+ View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);
+ mSecurityViewFlipper.addView(v);
+ updateSecurityView(v, mIsBouncing);
+ view = (KeyguardSecurityView)v;
+ }
+
+ if (view instanceof KeyguardSelectorView) {
+ KeyguardSelectorView selectorView = (KeyguardSelectorView) view;
+ View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container);
+ selectorView.setCarrierArea(carrierText);
+ }
+
+ return view;
+ }
+
+ private void updateSecurityView(View view, boolean isBouncing) {
+ mIsBouncing = isBouncing;
+ if (view instanceof KeyguardSecurityView) {
+ KeyguardSecurityView ksv = (KeyguardSecurityView) view;
+ ksv.setKeyguardCallback(mCallback);
+ ksv.setLockPatternUtils(mLockPatternUtils);
+ if (isBouncing) {
+ ksv.showBouncer(0);
+ } else {
+ ksv.hideBouncer(0);
+ }
+ } else {
+ Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
+ }
+ }
+
+ protected void onFinishInflate() {
+ mSecurityViewFlipper = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
+ mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
+ }
+
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ mSecurityModel.setLockPatternUtils(utils);
+ mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
+ }
+
+ private void showDialog(String title, String message) {
+ final AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(title)
+ .setMessage(message)
+ .setNeutralButton(R.string.ok, null)
+ .create();
+ if (!(mContext instanceof Activity)) {
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ dialog.show();
+ }
+
+ private void showTimeoutDialog() {
+ int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+ int messageId = 0;
+
+ switch (mSecurityModel.getSecurityMode()) {
+ case Pattern:
+ messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
+ break;
+ case PIN:
+ messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
+ break;
+ case Password:
+ messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
+ break;
+ // These don't have timeout dialogs.
+ case Account:
+ case Biometric:
+ case Invalid:
+ case None:
+ case SimPin:
+ case SimPuk:
+ break;
+ }
+
+ if (messageId != 0) {
+ final String message = mContext.getString(messageId,
+ KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
+ timeoutInSeconds);
+ showDialog(null, message);
+ }
+ }
+
+ private void showAlmostAtWipeDialog(int attempts, int remaining) {
+ String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
+ attempts, remaining);
+ showDialog(null, message);
+ }
+
+ private void showWipeDialog(int attempts) {
+ String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts);
+ showDialog(null, message);
+ }
+
+ private void showAlmostAtAccountLoginDialog() {
+ final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+ final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
+ - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
+ String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
+ count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
+ showDialog(null, message);
}
- KeyguardSecurityViewFlipper getFlipper() {
+ private void reportFailedUnlockAttempt() {
+ final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+ final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
+
+ if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
+
+ SecurityMode mode = mSecurityModel.getSecurityMode();
+ final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
+
+ final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
+ .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser());
+
+ final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
+ - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
+
+ final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
+ (failedAttemptsBeforeWipe - failedAttempts)
+ : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
+
+ boolean showTimeout = false;
+ if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
+ // If we reach this code, it means the user has installed a DevicePolicyManager
+ // that requests device wipe after N attempts. Once we get below the grace
+ // period, we'll post this dialog every time as a clear warning until the
+ // bombshell hits and the device is wiped.
+ if (remainingBeforeWipe > 0) {
+ showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
+ } else {
+ // Too many attempts. The device will be wiped shortly.
+ Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
+ showWipeDialog(failedAttempts);
+ }
+ } else {
+ showTimeout =
+ (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
+ if (usingPattern && mEnableFallback) {
+ if (failedAttempts == failedAttemptWarning) {
+ showAlmostAtAccountLoginDialog();
+ showTimeout = false; // don't show both dialogs
+ } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
+ mLockPatternUtils.setPermanentlyLocked(true);
+ showSecurityScreen(SecurityMode.Account);
+ // don't show timeout dialog because we show account unlock screen next
+ showTimeout = false;
+ }
+ }
+ }
+ monitor.reportFailedUnlockAttempt();
+ mLockPatternUtils.reportFailedPasswordAttempt();
+ if (showTimeout) {
+ showTimeoutDialog();
+ }
+ }
+
+ /**
+ * Shows the primary security screen for the user. This will be either the multi-selector
+ * or the user's security method.
+ * @param turningOff true if the device is being turned off
+ */
+ void showPrimarySecurityScreen(boolean turningOff) {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
+ if (!turningOff &&
+ KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
+ // If we're not turning off, then allow biometric alternate.
+ // We'll reload it when the device comes back on.
+ securityMode = mSecurityModel.getAlternateFor(securityMode);
+ }
+ showSecurityScreen(securityMode);
+ }
+
+ /**
+ * Shows the backup security screen for the current security mode. This could be used for
+ * password recovery screens but is currently only used for pattern unlock to show the
+ * account unlock screen and biometric unlock to show the user's normal unlock.
+ */
+ private void showBackupSecurityScreen() {
+ if (DEBUG) Log.d(TAG, "showBackupSecurity()");
+ SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection);
+ showSecurityScreen(backup);
+ }
+
+ private boolean showNextSecurityScreenIfPresent() {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ // Allow an alternate, such as biometric unlock
+ securityMode = mSecurityModel.getAlternateFor(securityMode);
+ if (SecurityMode.None == securityMode) {
+ return false;
+ } else {
+ showSecurityScreen(securityMode); // switch to the alternate security view
+ return true;
+ }
+ }
+
+ boolean showNextSecurityScreenOrFinish(boolean authenticated) {
+ if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
+ boolean finish = false;
+ if (SecurityMode.None == mCurrentSecuritySelection) {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ // Allow an alternate, such as biometric unlock
+ securityMode = mSecurityModel.getAlternateFor(securityMode);
+ if (SecurityMode.None == securityMode) {
+ finish = true; // no security required
+ } else {
+ showSecurityScreen(securityMode); // switch to the alternate security view
+ }
+ } else if (authenticated) {
+ switch (mCurrentSecuritySelection) {
+ case Pattern:
+ case Password:
+ case PIN:
+ case Account:
+ case Biometric:
+ finish = true;
+ break;
+
+ case SimPin:
+ case SimPuk:
+ // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ if (securityMode != SecurityMode.None) {
+ showSecurityScreen(securityMode);
+ } else {
+ finish = true;
+ }
+ break;
+
+ default:
+ Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
+ showPrimarySecurityScreen(false);
+ break;
+ }
+ } else {
+ showPrimarySecurityScreen(false);
+ }
+ if (finish) {
+ mSecurityCallback.finish();
+ }
+ return finish;
+ }
+
+ /**
+ * Switches to the given security view unless it's already being shown, in which case
+ * this is a no-op.
+ *
+ * @param securityMode
+ */
+ private void showSecurityScreen(SecurityMode securityMode) {
+ if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
+
+ if (securityMode == mCurrentSecuritySelection) return;
+
+ KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
+ KeyguardSecurityView newView = getSecurityView(securityMode);
+
+ // Emulate Activity life cycle
+ if (oldView != null) {
+ oldView.onPause();
+ oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
+ }
+ newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
+ newView.setKeyguardCallback(mCallback);
+
+ // Find and show this child.
+ final int childCount = mSecurityViewFlipper.getChildCount();
+
+ final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
+ for (int i = 0; i < childCount; i++) {
+ if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
+ mSecurityViewFlipper.setDisplayedChild(i);
+ break;
+ }
+ }
+
+ mCurrentSecuritySelection = securityMode;
+ }
+
+ private KeyguardSecurityViewFlipper getFlipper() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof KeyguardSecurityViewFlipper) {
@@ -41,5 +396,129 @@ public class KeyguardSecurityContainer extends FrameLayout {
flipper.hideBouncer(duration);
}
}
+
+ private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
+
+ public void userActivity(long timeout) {
+ if (mSecurityCallback != null) {
+ mSecurityCallback.userActivity(timeout);
+ }
+ }
+
+ public void dismiss(boolean authenticated) {
+ mSecurityCallback.dismiss(authenticated);
+ }
+
+ public boolean isVerifyUnlockOnly() {
+ return mIsVerifyUnlockOnly;
+ }
+
+ public void reportUnlockAttempt(boolean success) {
+ KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+ if (success) {
+ monitor.clearFailedUnlockAttempts();
+ mLockPatternUtils.reportSuccessfulPasswordAttempt();
+ } else {
+ if (mCurrentSecuritySelection == SecurityMode.Biometric) {
+ monitor.reportFailedBiometricUnlockAttempt();
+ } else {
+ KeyguardSecurityContainer.this.reportFailedUnlockAttempt();
+ }
+ }
+ }
+
+ @Override
+ public void showBackupSecurity() {
+ KeyguardSecurityContainer.this.showBackupSecurityScreen();
+ }
+
+ };
+
+ // The following is used to ignore callbacks from SecurityViews that are no longer current
+ // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
+ // state for the current security method.
+ private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
+ @Override
+ public void userActivity(long timeout) { }
+ @Override
+ public void showBackupSecurity() { }
+ @Override
+ public void reportUnlockAttempt(boolean success) { }
+ @Override
+ public boolean isVerifyUnlockOnly() { return false; }
+ @Override
+ public void dismiss(boolean securityVerified) { }
+ };
+
+ private int getSecurityViewIdForMode(SecurityMode securityMode) {
+ switch (securityMode) {
+ case None: return R.id.keyguard_selector_view;
+ case Pattern: return R.id.keyguard_pattern_view;
+ case PIN: return R.id.keyguard_pin_view;
+ case Password: return R.id.keyguard_password_view;
+ case Biometric: return R.id.keyguard_face_unlock_view;
+ case Account: return R.id.keyguard_account_view;
+ case SimPin: return R.id.keyguard_sim_pin_view;
+ case SimPuk: return R.id.keyguard_sim_puk_view;
+ }
+ return 0;
+ }
+
+ private int getLayoutIdFor(SecurityMode securityMode) {
+ switch (securityMode) {
+ case None: return R.layout.keyguard_selector_view;
+ case Pattern: return R.layout.keyguard_pattern_view;
+ case PIN: return R.layout.keyguard_pin_view;
+ case Password: return R.layout.keyguard_password_view;
+ case Biometric: return R.layout.keyguard_face_unlock_view;
+ case Account: return R.layout.keyguard_account_view;
+ case SimPin: return R.layout.keyguard_sim_pin_view;
+ case SimPuk: return R.layout.keyguard_sim_puk_view;
+ default:
+ return 0;
+ }
+ }
+
+ public SecurityMode getSecurityMode() {
+ return mSecurityModel.getSecurityMode();
+ }
+
+ public void verifyUnlock() {
+ mIsVerifyUnlockOnly = true;
+ showSecurityScreen(getSecurityMode());
+ }
+
+ public SecurityMode getCurrentSecuritySelection() {
+ return mCurrentSecuritySelection;
+ }
+
+ public void dismiss(boolean authenticated) {
+ mCallback.dismiss(authenticated);
+ }
+
+ public boolean needsInput() {
+ return mSecurityViewFlipper.needsInput();
+ }
+
+ @Override
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mSecurityViewFlipper.setKeyguardCallback(callback);
+ }
+
+ @Override
+ public void reset() {
+ mSecurityViewFlipper.reset();
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mSecurityViewFlipper.getCallback();
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ mSecurityViewFlipper.showUsabilityHint();
+ }
+
}