Fix 2332563: Add password-lock support to lockscreen
diff --git a/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 66c5159..ccb7902 100644
--- a/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -111,7 +111,12 @@
/**
* Unlock by entering an account's login and password.
*/
- Account
+ Account,
+
+ /**
+ * Unlock by entering a password or PIN
+ */
+ Password
}
/**
@@ -268,7 +273,7 @@
public void reportFailedPatternAttempt() {
mUpdateMonitor.reportFailedAttempt();
final int failedAttempts = mUpdateMonitor.getFailedAttempts();
- if (DEBUG) Log.d(TAG,
+ if (DEBUG) Log.d(TAG,
"reportFailedPatternAttempt: #" + failedAttempts +
" (enableFallback=" + mEnableFallback + ")");
if (mEnableFallback && failedAttempts ==
@@ -309,7 +314,7 @@
mLockScreen = createLockScreen();
addView(mLockScreen);
final UnlockMode unlockMode = getUnlockMode();
- if (DEBUG) Log.d(TAG,
+ if (DEBUG) Log.d(TAG,
"LockPatternKeyguardView ctor: about to createUnlockScreenFor; mEnableFallback="
+ mEnableFallback);
mUnlockScreen = createUnlockScreenFor(unlockMode);
@@ -434,16 +439,25 @@
private boolean isSecure() {
UnlockMode unlockMode = getUnlockMode();
- if (unlockMode == UnlockMode.Pattern) {
- return mLockPatternUtils.isLockPatternEnabled();
- } else if (unlockMode == UnlockMode.SimPin) {
- return mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED
- || mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
- } else if (unlockMode == UnlockMode.Account) {
- return true;
- } else {
- throw new IllegalStateException("unknown unlock mode " + unlockMode);
+ boolean secure = false;
+ switch (unlockMode) {
+ case Pattern:
+ secure = mLockPatternUtils.isLockPatternEnabled();
+ break;
+ case SimPin:
+ secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED
+ || mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
+ break;
+ case Account:
+ secure = true;
+ break;
+ case Password:
+ secure = mLockPatternUtils.isLockPasswordEnabled();
+ break;
+ default:
+ throw new IllegalStateException("unknown unlock mode " + unlockMode);
}
+ return secure;
}
private void updateScreen(final Mode mode) {
@@ -524,6 +538,12 @@
// "permanently locked" state.)
return createUnlockScreenFor(UnlockMode.Pattern);
}
+ } else if (unlockMode == UnlockMode.Password) {
+ return new PasswordUnlockScreen(
+ mContext,
+ mLockPatternUtils,
+ mUpdateMonitor,
+ mKeyguardScreenCallback);
} else {
throw new IllegalArgumentException("unknown unlock mode " + unlockMode);
}
@@ -575,13 +595,29 @@
*/
private UnlockMode getUnlockMode() {
final IccCard.State simState = mUpdateMonitor.getSimState();
+ UnlockMode currentMode;
if (simState == IccCard.State.PIN_REQUIRED || simState == IccCard.State.PUK_REQUIRED) {
- return UnlockMode.SimPin;
+ currentMode = UnlockMode.SimPin;
} else {
- return (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) ?
- UnlockMode.Account:
- UnlockMode.Pattern;
+ final int mode = mLockPatternUtils.getPasswordMode();
+ switch (mode) {
+ case LockPatternUtils.MODE_PIN:
+ case LockPatternUtils.MODE_PASSWORD:
+ currentMode = UnlockMode.Password;
+ break;
+ case LockPatternUtils.MODE_PATTERN:
+ // "forgot pattern" button is only available in the pattern mode...
+ if (mForgotPattern && mLockPatternUtils.isPermanentlyLocked()) {
+ currentMode = UnlockMode.Account;
+ } else {
+ currentMode = UnlockMode.Pattern;
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unknown unlock mode:" + mode);
+ }
}
+ return currentMode;
}
private void showTimeoutDialog() {
diff --git a/policy/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java b/policy/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
index 8a62cbd..ed5a058 100644
--- a/policy/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
+++ b/policy/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
@@ -50,12 +50,7 @@
}
public boolean isSecure() {
- return isLockPatternSecure() || isSimPinSecure();
- }
-
- private boolean isLockPatternSecure() {
- return mLockPatternUtils.isLockPatternEnabled() && mLockPatternUtils
- .savedPatternExists();
+ return mLockPatternUtils.isSecure() || isSimPinSecure();
}
private boolean isSimPinSecure() {
diff --git a/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java
new file mode 100644
index 0000000..bb1950a
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2010 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.content.Context;
+
+import com.android.internal.telephony.IccCard.State;
+import com.android.internal.widget.LockPatternUtils;
+
+import android.text.Editable;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.internal.R;
+
+/**
+ * Displays a dialer-like interface or alphanumeric (latin-1) key entry for the user to enter
+ * an unlock password
+ */
+public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen, View.OnClickListener,
+ KeyguardUpdateMonitor.ConfigurationChangeCallback, KeyguardUpdateMonitor.InfoCallback {
+
+ private static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
+
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final KeyguardScreenCallback mCallback;
+
+ private final boolean mCreatedWithKeyboardOpen;
+
+ private TextView mPasswordTextView;
+ private TextView mOkButton;
+ private TextView mEmergencyCallButton;
+ private View mBackSpaceButton;
+ private TextView mCarrier;
+ private LockPatternUtils mLockPatternUtils;
+ private Button mCancelButton;
+ private int mPasswordAttempts = 0;
+ private int mMinimumPasswordLength = 4; // TODO: get from policy store
+
+ private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+
+ public PasswordUnlockScreen(Context context, LockPatternUtils lockPatternUtils,
+ KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback) {
+ super(context);
+ mUpdateMonitor = updateMonitor;
+ mCallback = callback;
+ mCreatedWithKeyboardOpen = mUpdateMonitor.isKeyboardOpen();
+
+ LayoutInflater layoutInflater = LayoutInflater.from(context);
+ if (mCreatedWithKeyboardOpen) {
+ layoutInflater.inflate(R.layout.keyguard_screen_password_landscape, this, true);
+ } else {
+ layoutInflater.inflate(R.layout.keyguard_screen_password_portrait, this, true);
+ new TouchInput();
+ }
+
+ mPasswordTextView = (TextView) findViewById(R.id.pinDisplay);
+ mBackSpaceButton = findViewById(R.id.backspace);
+ mBackSpaceButton.setOnClickListener(this);
+
+ // The cancel button is not used on this screen.
+ mCancelButton = (Button) findViewById(R.id.cancel);
+ if (mCancelButton != null) {
+ mCancelButton.setText("");
+ }
+
+ mEmergencyCallButton = (TextView) findViewById(R.id.emergencyCall);
+ mOkButton = (TextView) findViewById(R.id.ok);
+
+ mPasswordTextView.setFocusable(false);
+
+ mEmergencyCallButton.setOnClickListener(this);
+ mOkButton.setOnClickListener(this);
+
+ mUpdateMonitor.registerConfigurationChangeCallback(this);
+
+ mLockPatternUtils = lockPatternUtils;
+ mCarrier = (TextView) findViewById(R.id.carrier);
+ // until we get an update...
+ mCarrier.setText(LockScreen.getCarrierString(mUpdateMonitor.getTelephonyPlmn(),
+ mUpdateMonitor.getTelephonySpn()));
+
+ updateMonitor.registerInfoCallback(this);
+ updateMonitor.registerConfigurationChangeCallback(this);
+
+ setFocusableInTouchMode(true);
+ }
+
+ /** {@inheritDoc} */
+ public boolean needsInput() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public void onPause() {
+
+ }
+
+ /** {@inheritDoc} */
+ public void onResume() {
+ // start fresh
+ mPasswordTextView.setText("");
+ }
+
+ /** {@inheritDoc} */
+ public void cleanUp() {
+ mUpdateMonitor.removeCallback(this);
+ }
+
+ public void onClick(View v) {
+ if (v == mBackSpaceButton) {
+ final Editable digits = mPasswordTextView.getEditableText();
+ final int len = digits.length();
+ if (len > 0) {
+ digits.delete(len-1, len);
+ }
+ } else if (v == mEmergencyCallButton) {
+ mCallback.takeEmergencyCallAction();
+ } else if (v == mOkButton) {
+ verifyPasswordAndUnlock();
+ }
+ mCallback.pokeWakelock();
+ }
+
+ private void verifyPasswordAndUnlock() {
+ String entry = mPasswordTextView.getText().toString();
+ if (mLockPatternUtils.checkPassword(entry)) {
+ mPasswordAttempts = 0;
+ mCallback.keyguardDone(true);
+ } else if (entry.length() >= mMinimumPasswordLength ) {
+ // to avoid accidental lockout, only count attempts that are long enough to be a
+ // real password. This may require some tweaking.
+ mPasswordAttempts++;
+ }
+ mPasswordTextView.setText("");
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ return true;
+ }
+
+ final char match = event.getMatch(DIGITS);
+ if (match != 0) {
+ reportDigit(match - '0');
+ return true;
+ }
+ if (keyCode == KeyEvent.KEYCODE_DEL) {
+ mPasswordTextView.onKeyDown(keyCode, event);
+ return true;
+ }
+
+ if (keyCode == KeyEvent.KEYCODE_ENTER) {
+ verifyPasswordAndUnlock();
+ return true;
+ }
+
+ return false;
+ }
+
+ private void reportDigit(int digit) {
+ mPasswordTextView.append(Integer.toString(digit));
+ }
+
+ public void onOrientationChange(boolean inPortrait) {
+
+ }
+
+ public void onKeyboardChange(boolean isKeyboardOpen) {
+ if (isKeyboardOpen != mCreatedWithKeyboardOpen) {
+ mCallback.recreateMe();
+ }
+ }
+
+ /**
+ * Helper class to handle input from touch dialer. Only relevant when
+ * the keyboard is shut.
+ */
+ private class TouchInput implements View.OnClickListener {
+ private int mDigitIds[] = { R.id.zero, R.id.one, R.id.two, R.id.three, R.id.four,
+ R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine };
+ private TextView mCancelButton;
+ private TouchInput() {
+ for (int i = 0; i < mDigitIds.length; i++) {
+ Button button = (Button) findViewById(mDigitIds[i]);
+ button.setOnClickListener(this);
+ button.setText(Integer.toString(i));
+ }
+ mCancelButton = (TextView) findViewById(R.id.cancel);
+ mCancelButton.setOnClickListener(this);
+ mOkButton = (TextView) findViewById(R.id.ok);
+ mOkButton.setOnClickListener(this);
+ }
+
+ public void onClick(View v) {
+ if (v == mCancelButton) {
+ return;
+ }
+ if (v == mOkButton) {
+ verifyPasswordAndUnlock();
+ }
+
+ final int digit = checkDigit(v);
+ if (digit >= 0) {
+ mCallback.pokeWakelock(DIGIT_PRESS_WAKE_MILLIS);
+ reportDigit(digit);
+ }
+ }
+
+ private int checkDigit(View v) {
+ int digit = -1;
+ for (int i = 0; i < mDigitIds.length; i++) {
+ if (v.getId() == mDigitIds[i]) {
+ digit = i;
+ break;
+ }
+ }
+ return digit;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+ mCarrier.setText(LockScreen.getCarrierString(plmn, spn));
+ }
+
+ public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
+
+ }
+
+ public void onRingerModeChanged(int state) {
+
+ }
+
+ public void onTimeChanged() {
+
+ }
+
+ public void onSimStateChanged(State simState) {
+
+ }
+}
diff --git a/policy/com/android/internal/policy/impl/SimUnlockScreen.java b/policy/com/android/internal/policy/impl/SimUnlockScreen.java
index 3881d11..8c738a7 100644
--- a/policy/com/android/internal/policy/impl/SimUnlockScreen.java
+++ b/policy/com/android/internal/policy/impl/SimUnlockScreen.java
@@ -27,7 +27,6 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
-import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.R;
@@ -167,6 +166,7 @@
}
mCallback.pokeWakelock();
} else if (v == mEmergencyCallButton) {
+ mCallback.pokeWakelock();
mCallback.takeEmergencyCallAction();
} else if (v == mOkButton) {
checkPin();
@@ -219,8 +219,8 @@
mHeaderText.setText(R.string.keyguard_password_wrong_pin_code);
mPinText.setText("");
mEnteredDigits = 0;
- mCallback.pokeWakelock();
}
+ mCallback.pokeWakelock();
}
}.start();
}
diff --git a/policy/com/android/internal/policy/impl/UnlockScreen.java b/policy/com/android/internal/policy/impl/UnlockScreen.java
index e090ac5..30ab879 100644
--- a/policy/com/android/internal/policy/impl/UnlockScreen.java
+++ b/policy/com/android/internal/policy/impl/UnlockScreen.java
@@ -197,6 +197,7 @@
// emergency call buttons
final OnClickListener emergencyClick = new OnClickListener() {
public void onClick(View v) {
+ mCallback.pokeWakelock();
mCallback.takeEmergencyCallAction();
}
};