blob: 6b8be694b97d6cb260fa49733241385634dee114 [file] [log] [blame]
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001/*
Jim Millerdcb3d842012-08-23 19:18:12 -07002 * Copyright (C) 2012 The Android Open Source Project
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Jim Miller5ecd8112013-01-09 18:50:26 -080016package com.android.keyguard;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080017
Fred Quintana4e8f62f2009-03-24 22:48:13 -070018import android.accounts.Account;
19import android.accounts.AccountManager;
Jim Millerdcb3d842012-08-23 19:18:12 -070020import android.accounts.AccountManagerCallback;
Fred Quintana1c01c8bc2009-08-15 21:46:24 -070021import android.accounts.AccountManagerFuture;
22import android.accounts.AuthenticatorException;
Jim Millerdcb3d842012-08-23 19:18:12 -070023import android.accounts.OperationCanceledException;
24import android.app.Dialog;
25import android.app.ProgressDialog;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080026import android.content.Context;
27import android.content.Intent;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080028import android.graphics.Rect;
Jim Millerdcb3d842012-08-23 19:18:12 -070029import android.os.Bundle;
Amith Yamasani2c7bc262012-11-05 16:46:02 -080030import android.os.UserHandle;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080031import android.text.Editable;
32import android.text.InputFilter;
33import android.text.LoginFilter;
34import android.text.TextWatcher;
Jim Millerdcb3d842012-08-23 19:18:12 -070035import android.util.AttributeSet;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080036import android.view.KeyEvent;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080037import android.view.View;
Karl Rosaen84999d62009-08-24 17:18:02 -070038import android.view.WindowManager;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080039import android.widget.Button;
40import android.widget.EditText;
Jim Millerdcb3d842012-08-23 19:18:12 -070041import android.widget.LinearLayout;
42
43import com.android.internal.widget.LockPatternUtils;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080044
Fred Quintana1c01c8bc2009-08-15 21:46:24 -070045import java.io.IOException;
46
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080047/**
48 * When the user forgets their password a bunch of times, we fall back on their
49 * account's login/password to unlock the phone (and reset their lock pattern).
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080050 */
Jim Millerdcb3d842012-08-23 19:18:12 -070051public class KeyguardAccountView extends LinearLayout implements KeyguardSecurityView,
Jim Miller6b05d582011-07-18 13:09:59 -070052 View.OnClickListener, TextWatcher {
Jim Millerdcb3d842012-08-23 19:18:12 -070053 private static final int AWAKE_POKE_MILLIS = 30000;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080054 private static final String LOCK_PATTERN_PACKAGE = "com.android.settings";
Brian Colonnaaae641b2011-09-23 14:37:11 -040055 private static final String LOCK_PATTERN_CLASS = LOCK_PATTERN_PACKAGE + ".ChooseLockGeneric";
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080056
Jim Millerdcb3d842012-08-23 19:18:12 -070057 private KeyguardSecurityCallback mCallback;
Jim Miller8b886fa2011-01-13 17:56:35 -080058 private LockPatternUtils mLockPatternUtils;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080059 private EditText mLogin;
60 private EditText mPassword;
61 private Button mOk;
Jim Millerdcb3d842012-08-23 19:18:12 -070062 public boolean mEnableFallback;
Jim Miller0ff7f012012-10-11 20:40:01 -070063 private SecurityMessageDisplay mSecurityMessageDisplay;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080064
65 /**
Karl Rosaen84999d62009-08-24 17:18:02 -070066 * Shown while making asynchronous check of password.
67 */
68 private ProgressDialog mCheckingDialog;
69
Jim Millerdcb3d842012-08-23 19:18:12 -070070 public KeyguardAccountView(Context context) {
71 this(context, null, 0);
72 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080073
Jim Millerdcb3d842012-08-23 19:18:12 -070074 public KeyguardAccountView(Context context, AttributeSet attrs) {
75 this(context, attrs, 0);
76 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080077
Jim Millerdcb3d842012-08-23 19:18:12 -070078 public KeyguardAccountView(Context context, AttributeSet attrs, int defStyle) {
79 super(context, attrs, defStyle);
80 mLockPatternUtils = new LockPatternUtils(getContext());
81 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080082
Jim Millerdcb3d842012-08-23 19:18:12 -070083 @Override
84 protected void onFinishInflate() {
85 super.onFinishInflate();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080086
87 mLogin = (EditText) findViewById(R.id.login);
88 mLogin.setFilters(new InputFilter[] { new LoginFilter.UsernameFilterGeneric() } );
89 mLogin.addTextChangedListener(this);
90
91 mPassword = (EditText) findViewById(R.id.password);
92 mPassword.addTextChangedListener(this);
93
94 mOk = (Button) findViewById(R.id.ok);
95 mOk.setOnClickListener(this);
Jim Miller0b728242012-10-28 19:42:30 -070096
97 mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
Jim Millerdcb3d842012-08-23 19:18:12 -070098 reset();
Fred Quintana4e8f62f2009-03-24 22:48:13 -070099 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800100
Jim Millerdcb3d842012-08-23 19:18:12 -0700101 public void setKeyguardCallback(KeyguardSecurityCallback callback) {
102 mCallback = callback;
103 }
104
105 public void setLockPatternUtils(LockPatternUtils utils) {
106 mLockPatternUtils = utils;
107 }
108
109 public KeyguardSecurityCallback getCallback() {
110 return mCallback;
111 }
112
113
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800114 public void afterTextChanged(Editable s) {
115 }
116
117 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
118 }
119
120 public void onTextChanged(CharSequence s, int start, int before, int count) {
Jim Millerdcb3d842012-08-23 19:18:12 -0700121 if (mCallback != null) {
122 mCallback.userActivity(AWAKE_POKE_MILLIS);
123 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800124 }
125
126 @Override
127 protected boolean onRequestFocusInDescendants(int direction,
128 Rect previouslyFocusedRect) {
129 // send focus to the login field
130 return mLogin.requestFocus(direction, previouslyFocusedRect);
131 }
132
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800133 public boolean needsInput() {
134 return true;
135 }
136
Jim Millerdcb3d842012-08-23 19:18:12 -0700137 public void reset() {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800138 // start fresh
139 mLogin.setText("");
140 mPassword.setText("");
141 mLogin.requestFocus();
Adam Cohen0a4f9002012-10-12 19:57:16 -0700142 boolean permLocked = mLockPatternUtils.isPermanentlyLocked();
143 mSecurityMessageDisplay.setMessage(permLocked ? R.string.kg_login_too_many_attempts :
144 R.string.kg_login_instructions, permLocked ? true : false);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800145 }
146
147 /** {@inheritDoc} */
148 public void cleanUp() {
Karl Rosaen84999d62009-08-24 17:18:02 -0700149 if (mCheckingDialog != null) {
150 mCheckingDialog.hide();
151 }
Jim Miller8b886fa2011-01-13 17:56:35 -0800152 mCallback = null;
153 mLockPatternUtils = null;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800154 }
155
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800156 public void onClick(View v) {
Jim Millerdcb3d842012-08-23 19:18:12 -0700157 mCallback.userActivity(0);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800158 if (v == mOk) {
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700159 asyncCheckPassword();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800160 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800161 }
162
Jim Miller1d587e32010-05-06 16:31:34 -0700163 private void postOnCheckPasswordResult(final boolean success) {
164 // ensure this runs on UI thread
165 mLogin.post(new Runnable() {
166 public void run() {
167 if (success) {
168 // clear out forgotten password
169 mLockPatternUtils.setPermanentlyLocked(false);
170 mLockPatternUtils.setLockPatternEnabled(false);
171 mLockPatternUtils.saveLockPattern(null);
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700172
Jim Miller1d587e32010-05-06 16:31:34 -0700173 // launch the 'choose lock pattern' activity so
174 // the user can pick a new one if they want to
175 Intent intent = new Intent();
176 intent.setClassName(LOCK_PATTERN_PACKAGE, LOCK_PATTERN_CLASS);
177 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Amith Yamasani2c7bc262012-11-05 16:46:02 -0800178 mContext.startActivityAsUser(intent,
179 new UserHandle(mLockPatternUtils.getCurrentUser()));
Jim Miller1d587e32010-05-06 16:31:34 -0700180 mCallback.reportSuccessfulUnlockAttempt();
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700181
Jim Millerdcb3d842012-08-23 19:18:12 -0700182 // dismiss keyguard
183 mCallback.dismiss(true);
Jim Miller1d587e32010-05-06 16:31:34 -0700184 } else {
Adam Cohen0a4f9002012-10-12 19:57:16 -0700185 mSecurityMessageDisplay.setMessage(R.string.kg_login_invalid_input, true);
Jim Miller1d587e32010-05-06 16:31:34 -0700186 mPassword.setText("");
187 mCallback.reportFailedUnlockAttempt();
188 }
189 }
190 });
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700191 }
192
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800193 @Override
194 public boolean dispatchKeyEvent(KeyEvent event) {
195 if (event.getAction() == KeyEvent.ACTION_DOWN
196 && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Karl Rosaen84999d62009-08-24 17:18:02 -0700197 if (mLockPatternUtils.isPermanentlyLocked()) {
Jim Millerdcb3d842012-08-23 19:18:12 -0700198 mCallback.dismiss(false);
Karl Rosaen84999d62009-08-24 17:18:02 -0700199 } else {
Jim Millerdcb3d842012-08-23 19:18:12 -0700200 // TODO: mCallback.forgotPattern(false);
Karl Rosaen84999d62009-08-24 17:18:02 -0700201 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800202 return true;
203 }
204 return super.dispatchKeyEvent(event);
205 }
206
207 /**
208 * Given the string the user entered in the 'username' field, find
209 * the stored account that they probably intended. Prefer, in order:
210 *
211 * - an exact match for what was typed, or
212 * - a case-insensitive match for what was typed, or
213 * - if they didn't include a domain, an exact match of the username, or
214 * - if they didn't include a domain, a case-insensitive
215 * match of the username.
216 *
217 * If there is a tie for the best match, choose neither --
218 * the user needs to be more specific.
219 *
220 * @return an account name from the database, or null if we can't
221 * find a single best match.
222 */
Fred Quintana4e8f62f2009-03-24 22:48:13 -0700223 private Account findIntendedAccount(String username) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -0800224 Account[] accounts = AccountManager.get(mContext).getAccountsByTypeAsUser("com.google",
225 new UserHandle(mLockPatternUtils.getCurrentUser()));
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800226
227 // Try to figure out which account they meant if they
228 // typed only the username (and not the domain), or got
229 // the case wrong.
230
Fred Quintana4e8f62f2009-03-24 22:48:13 -0700231 Account bestAccount = null;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800232 int bestScore = 0;
Fred Quintana4e8f62f2009-03-24 22:48:13 -0700233 for (Account a: accounts) {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800234 int score = 0;
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700235 if (username.equals(a.name)) {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800236 score = 4;
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700237 } else if (username.equalsIgnoreCase(a.name)) {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800238 score = 3;
239 } else if (username.indexOf('@') < 0) {
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700240 int i = a.name.indexOf('@');
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800241 if (i >= 0) {
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700242 String aUsername = a.name.substring(0, i);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800243 if (username.equals(aUsername)) {
244 score = 2;
245 } else if (username.equalsIgnoreCase(aUsername)) {
246 score = 1;
247 }
248 }
249 }
250 if (score > bestScore) {
251 bestAccount = a;
252 bestScore = score;
253 } else if (score == bestScore) {
254 bestAccount = null;
255 }
256 }
257 return bestAccount;
258 }
259
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700260 private void asyncCheckPassword() {
Jim Millerdcb3d842012-08-23 19:18:12 -0700261 mCallback.userActivity(AWAKE_POKE_MILLIS);
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800262 final String login = mLogin.getText().toString();
263 final String password = mPassword.getText().toString();
Fred Quintana4e8f62f2009-03-24 22:48:13 -0700264 Account account = findIntendedAccount(login);
265 if (account == null) {
Jim Miller1d587e32010-05-06 16:31:34 -0700266 postOnCheckPasswordResult(false);
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700267 return;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800268 }
Karl Rosaen84999d62009-08-24 17:18:02 -0700269 getProgressDialog().show();
Fred Quintana261259b2009-10-02 17:19:24 -0700270 Bundle options = new Bundle();
271 options.putString(AccountManager.KEY_PASSWORD, password);
Amith Yamasani2c7bc262012-11-05 16:46:02 -0800272 AccountManager.get(mContext).confirmCredentialsAsUser(account, options, null /* activity */,
Fred Quintana261259b2009-10-02 17:19:24 -0700273 new AccountManagerCallback<Bundle>() {
274 public void run(AccountManagerFuture<Bundle> future) {
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700275 try {
Jim Millerdcb3d842012-08-23 19:18:12 -0700276 mCallback.userActivity(AWAKE_POKE_MILLIS);
Fred Quintana261259b2009-10-02 17:19:24 -0700277 final Bundle result = future.getResult();
278 final boolean verified = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Jim Miller1d587e32010-05-06 16:31:34 -0700279 postOnCheckPasswordResult(verified);
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700280 } catch (OperationCanceledException e) {
Jim Miller1d587e32010-05-06 16:31:34 -0700281 postOnCheckPasswordResult(false);
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700282 } catch (IOException e) {
Jim Miller1d587e32010-05-06 16:31:34 -0700283 postOnCheckPasswordResult(false);
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700284 } catch (AuthenticatorException e) {
Jim Miller1d587e32010-05-06 16:31:34 -0700285 postOnCheckPasswordResult(false);
Karl Rosaen84999d62009-08-24 17:18:02 -0700286 } finally {
287 mLogin.post(new Runnable() {
288 public void run() {
289 getProgressDialog().hide();
290 }
291 });
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700292 }
Fred Quintana1c01c8bc2009-08-15 21:46:24 -0700293 }
Amith Yamasani2c7bc262012-11-05 16:46:02 -0800294 }, null /* handler */, new UserHandle(mLockPatternUtils.getCurrentUser()));
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800295 }
Karl Rosaen84999d62009-08-24 17:18:02 -0700296
297 private Dialog getProgressDialog() {
298 if (mCheckingDialog == null) {
299 mCheckingDialog = new ProgressDialog(mContext);
300 mCheckingDialog.setMessage(
Jim Millerdcb3d842012-08-23 19:18:12 -0700301 mContext.getString(R.string.kg_login_checking_password));
Karl Rosaen84999d62009-08-24 17:18:02 -0700302 mCheckingDialog.setIndeterminate(true);
303 mCheckingDialog.setCancelable(false);
304 mCheckingDialog.getWindow().setType(
305 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Karl Rosaen84999d62009-08-24 17:18:02 -0700306 }
307 return mCheckingDialog;
308 }
Jim Millerdcb3d842012-08-23 19:18:12 -0700309
310 @Override
311 public void onPause() {
312
313 }
314
315 @Override
Chris Wrena042ac92012-11-07 11:37:06 -0500316 public void onResume(int reason) {
Jim Millerdcb3d842012-08-23 19:18:12 -0700317 reset();
318 }
319
Jim Miller0ff7f012012-10-11 20:40:01 -0700320 @Override
Adam Cohen6fb841f2012-10-24 13:15:38 -0700321 public void showUsabilityHint() {
322 }
Chris Wrenc0ae9e62012-11-05 13:16:31 -0500323
324 @Override
325 public void showBouncer(int duration) {
326 }
327
328 @Override
329 public void hideBouncer(int duration) {
330 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800331}
Jim Millerdcb3d842012-08-23 19:18:12 -0700332