LockPatternKeyguardView.java revision 1c18828d20807342d37000746b18a3c1696f3b2e
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
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 */
16
17package com.android.internal.policy.impl;
18
19import com.android.internal.R;
20import com.android.internal.policy.impl.LockPatternKeyguardView.UnlockMode;
21import com.android.internal.telephony.IccCard;
22import com.android.internal.widget.LockPatternUtils;
23import com.android.internal.widget.LockScreenWidgetCallback;
24import com.android.internal.widget.TransportControlView;
25
26import android.accounts.Account;
27import android.accounts.AccountManager;
28import android.accounts.AccountManagerCallback;
29import android.accounts.AccountManagerFuture;
30import android.accounts.AuthenticatorException;
31import android.accounts.OperationCanceledException;
32import android.app.AlertDialog;
33import android.app.admin.DevicePolicyManager;
34import android.content.Context;
35import android.content.Intent;
36import android.content.res.Configuration;
37import android.content.res.Resources;
38import android.graphics.Bitmap;
39import android.graphics.Canvas;
40import android.graphics.ColorFilter;
41import android.graphics.PixelFormat;
42import android.graphics.drawable.Drawable;
43import android.os.Bundle;
44import android.os.SystemClock;
45import android.os.SystemProperties;
46import android.telephony.TelephonyManager;
47import android.text.TextUtils;
48import android.util.Log;
49import android.util.Slog;
50import android.view.KeyEvent;
51import android.view.MotionEvent;
52import android.view.View;
53import android.view.WindowManager;
54import android.view.accessibility.AccessibilityManager;
55
56import java.io.IOException;
57
58/**
59 * The host view for all of the screens of the pattern unlock screen.  There are
60 * two {@link Mode}s of operation, lock and unlock.  This will show the appropriate
61 * screen, and listen for callbacks via
62 * {@link com.android.internal.policy.impl.KeyguardScreenCallback}
63 * from the current screen.
64 *
65 * This view, in turn, communicates back to
66 * {@link com.android.internal.policy.impl.KeyguardViewManager}
67 * via its {@link com.android.internal.policy.impl.KeyguardViewCallback}, as appropriate.
68 */
69public class LockPatternKeyguardView extends KeyguardViewBase {
70
71    private static final int TRANSPORT_USERACTIVITY_TIMEOUT = 10000;
72
73    static final boolean DEBUG_CONFIGURATION = false;
74
75    // time after launching EmergencyDialer before the screen goes blank.
76    private static final int EMERGENCY_CALL_TIMEOUT = 10000;
77
78    // intent action for launching emergency dialer activity.
79    static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
80
81    private static final boolean DEBUG = false;
82    private static final String TAG = "LockPatternKeyguardView";
83
84    private final KeyguardUpdateMonitor mUpdateMonitor;
85    private final KeyguardWindowController mWindowController;
86
87    private View mLockScreen;
88    private View mUnlockScreen;
89
90    private boolean mScreenOn = false;
91    private boolean mEnableFallback = false; // assume no fallback UI until we know better
92
93    private boolean mShowLockBeforeUnlock = false;
94
95    /**
96     * The current {@link KeyguardScreen} will use this to communicate back to us.
97     */
98    KeyguardScreenCallback mKeyguardScreenCallback;
99
100
101    private boolean mRequiresSim;
102
103
104    /**
105     * Either a lock screen (an informational keyguard screen), or an unlock
106     * screen (a means for unlocking the device) is shown at any given time.
107     */
108    enum Mode {
109        LockScreen,
110        UnlockScreen
111    }
112
113    /**
114     * The different types screens available for {@link Mode#UnlockScreen}.
115     * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode()
116     */
117    enum UnlockMode {
118
119        /**
120         * Unlock by drawing a pattern.
121         */
122        Pattern,
123
124        /**
125         * Unlock by entering a sim pin.
126         */
127        SimPin,
128
129        /**
130         * Unlock by entering a sim puk.
131         */
132        SimPuk,
133
134        /**
135         * Unlock by entering an account's login and password.
136         */
137        Account,
138
139        /**
140         * Unlock by entering a password or PIN
141         */
142        Password,
143
144        /**
145         * Unknown (uninitialized) value
146         */
147        Unknown
148    }
149
150    /**
151     * The current mode.
152     */
153    private Mode mMode = Mode.LockScreen;
154
155    /**
156     * Keeps track of what mode the current unlock screen is (cached from most recent computation in
157     * {@link #getUnlockMode}).
158     */
159    private UnlockMode mUnlockScreenMode = UnlockMode.Unknown;
160
161    private boolean mForgotPattern;
162
163    /**
164     * If true, it means we are in the process of verifying that the user
165     * can get past the lock screen per {@link #verifyUnlock()}
166     */
167    private boolean mIsVerifyUnlockOnly = false;
168
169
170    /**
171     * Used to lookup the state of the lock pattern
172     */
173    private final LockPatternUtils mLockPatternUtils;
174
175    /**
176     * The current configuration.
177     */
178    private Configuration mConfiguration;
179
180    private Runnable mRecreateRunnable = new Runnable() {
181        public void run() {
182            updateScreen(mMode, false);
183        }
184    };
185
186    private LockScreenWidgetCallback mWidgetCallback = new LockScreenWidgetCallback() {
187        public void userActivity(View self) {
188            mKeyguardScreenCallback.pokeWakelock(TRANSPORT_USERACTIVITY_TIMEOUT);
189        }
190
191        public void requestShow(View view) {
192            if (DEBUG) Log.v(TAG, "View " + view + " requested show transports");
193            view.setVisibility(View.VISIBLE);
194        }
195
196        public void requestHide(View view) {
197            if (DEBUG) Log.v(TAG, "View " + view + " requested hide transports");
198            view.setVisibility(View.GONE);
199        }
200    };
201
202    /**
203     * @return Whether we are stuck on the lock screen because the sim is
204     *   missing.
205     */
206    private boolean stuckOnLockScreenBecauseSimMissing() {
207        return mRequiresSim
208                && (!mUpdateMonitor.isDeviceProvisioned())
209                && (mUpdateMonitor.getSimState() == IccCard.State.ABSENT ||
210                    mUpdateMonitor.getSimState() == IccCard.State.PERM_DISABLED);
211    }
212
213    /**
214     * @param context Used to inflate, and create views.
215     * @param updateMonitor Knows the state of the world, and passed along to each
216     *   screen so they can use the knowledge, and also register for callbacks
217     *   on dynamic information.
218     * @param lockPatternUtils Used to look up state of lock pattern.
219     */
220    public LockPatternKeyguardView(
221            Context context,
222            KeyguardUpdateMonitor updateMonitor,
223            LockPatternUtils lockPatternUtils,
224            KeyguardWindowController controller) {
225        super(context);
226
227        mConfiguration = context.getResources().getConfiguration();
228        mEnableFallback = false;
229        mRequiresSim = TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim"));
230        mUpdateMonitor = updateMonitor;
231        mLockPatternUtils = lockPatternUtils;
232        mWindowController = controller;
233
234        mKeyguardScreenCallback = new KeyguardScreenCallback() {
235
236            public void goToLockScreen() {
237                mForgotPattern = false;
238                if (mIsVerifyUnlockOnly) {
239                    // navigating away from unlock screen during verify mode means
240                    // we are done and the user failed to authenticate.
241                    mIsVerifyUnlockOnly = false;
242                    getCallback().keyguardDone(false);
243                } else {
244                    updateScreen(Mode.LockScreen, false);
245                }
246            }
247
248            public void goToUnlockScreen() {
249                final IccCard.State simState = mUpdateMonitor.getSimState();
250                if (stuckOnLockScreenBecauseSimMissing()
251                         || (simState == IccCard.State.PUK_REQUIRED
252                             && !mLockPatternUtils.isPukUnlockScreenEnable())){
253                    // stuck on lock screen when sim missing or
254                    // puk'd but puk unlock screen is disabled
255                    return;
256                }
257                if (!isSecure()) {
258                    getCallback().keyguardDone(true);
259                } else {
260                    updateScreen(Mode.UnlockScreen, false);
261                }
262            }
263
264            public void forgotPattern(boolean isForgotten) {
265                if (mEnableFallback) {
266                    mForgotPattern = isForgotten;
267                    updateScreen(Mode.UnlockScreen, false);
268                }
269            }
270
271            public boolean isSecure() {
272                return LockPatternKeyguardView.this.isSecure();
273            }
274
275            public boolean isVerifyUnlockOnly() {
276                return mIsVerifyUnlockOnly;
277            }
278
279            public void recreateMe(Configuration config) {
280                removeCallbacks(mRecreateRunnable);
281                post(mRecreateRunnable);
282            }
283
284            public void takeEmergencyCallAction() {
285                pokeWakelock(EMERGENCY_CALL_TIMEOUT);
286                if (TelephonyManager.getDefault().getCallState()
287                        == TelephonyManager.CALL_STATE_OFFHOOK) {
288                    mLockPatternUtils.resumeCall();
289                } else {
290                    Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
291                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
292                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
293                    getContext().startActivity(intent);
294                }
295            }
296
297            public void pokeWakelock() {
298                getCallback().pokeWakelock();
299            }
300
301            public void pokeWakelock(int millis) {
302                getCallback().pokeWakelock(millis);
303            }
304
305            public void keyguardDone(boolean authenticated) {
306                getCallback().keyguardDone(authenticated);
307            }
308
309            public void keyguardDoneDrawing() {
310                // irrelevant to keyguard screen, they shouldn't be calling this
311            }
312
313            public void reportFailedUnlockAttempt() {
314                mUpdateMonitor.reportFailedAttempt();
315                final int failedAttempts = mUpdateMonitor.getFailedAttempts();
316                if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts +
317                    " (enableFallback=" + mEnableFallback + ")");
318
319                final boolean usingPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
320                        == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
321
322                final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
323                        .getMaximumFailedPasswordsForWipe(null);
324
325                final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
326                        - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
327
328                final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
329                        (failedAttemptsBeforeWipe - failedAttempts)
330                        : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
331
332                if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
333                    // If we reach this code, it means the user has installed a DevicePolicyManager
334                    // that requests device wipe after N attempts.  Once we get below the grace
335                    // period, we'll post this dialog every time as a clear warning until the
336                    // bombshell hits and the device is wiped.
337                    if (remainingBeforeWipe > 0) {
338                        showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
339                    } else {
340                        // Too many attempts. The device will be wiped shortly.
341                        Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
342                        showWipeDialog(failedAttempts);
343                    }
344                } else if (usingPattern && mEnableFallback) {
345                    if (failedAttempts == failedAttemptWarning) {
346                        showAlmostAtAccountLoginDialog();
347                    } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
348                        mLockPatternUtils.setPermanentlyLocked(true);
349                        updateScreen(mMode, false);
350                    }
351                } else {
352                    final boolean showTimeout =
353                        (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
354                    if (showTimeout) {
355                        showTimeoutDialog();
356                    }
357                }
358                mLockPatternUtils.reportFailedPasswordAttempt();
359            }
360
361            public boolean doesFallbackUnlockScreenExist() {
362                return mEnableFallback;
363            }
364
365            public void reportSuccessfulUnlockAttempt() {
366                mLockPatternUtils.reportSuccessfulPasswordAttempt();
367            }
368        };
369
370        /**
371         * We'll get key events the current screen doesn't use. see
372         * {@link KeyguardViewBase#onKeyDown(int, android.view.KeyEvent)}
373         */
374        setFocusableInTouchMode(true);
375        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
376
377        updateScreen(getInitialMode(), false);
378        maybeEnableFallback(context);
379    }
380
381    private class AccountAnalyzer implements AccountManagerCallback<Bundle> {
382        private final AccountManager mAccountManager;
383        private final Account[] mAccounts;
384        private int mAccountIndex;
385
386        private AccountAnalyzer(AccountManager accountManager) {
387            mAccountManager = accountManager;
388            mAccounts = accountManager.getAccountsByType("com.google");
389        }
390
391        private void next() {
392            // if we are ready to enable the fallback or if we depleted the list of accounts
393            // then finish and get out
394            if (mEnableFallback || mAccountIndex >= mAccounts.length) {
395                if (mUnlockScreen == null) {
396                    Log.w(TAG, "no unlock screen when trying to enable fallback");
397                } else if (mUnlockScreen instanceof PatternUnlockScreen) {
398                    ((PatternUnlockScreen)mUnlockScreen).setEnableFallback(mEnableFallback);
399                }
400                return;
401            }
402
403            // lookup the confirmCredentials intent for the current account
404            mAccountManager.confirmCredentials(mAccounts[mAccountIndex], null, null, this, null);
405        }
406
407        public void start() {
408            mEnableFallback = false;
409            mAccountIndex = 0;
410            next();
411        }
412
413        public void run(AccountManagerFuture<Bundle> future) {
414            try {
415                Bundle result = future.getResult();
416                if (result.getParcelable(AccountManager.KEY_INTENT) != null) {
417                    mEnableFallback = true;
418                }
419            } catch (OperationCanceledException e) {
420                // just skip the account if we are unable to query it
421            } catch (IOException e) {
422                // just skip the account if we are unable to query it
423            } catch (AuthenticatorException e) {
424                // just skip the account if we are unable to query it
425            } finally {
426                mAccountIndex++;
427                next();
428            }
429        }
430    }
431
432    private void maybeEnableFallback(Context context) {
433        // Ask the account manager if we have an account that can be used as a
434        // fallback in case the user forgets his pattern.
435        AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context));
436        accountAnalyzer.start();
437    }
438
439
440    // TODO:
441    // This overloaded method was added to workaround a race condition in the framework between
442    // notification for orientation changed, layout() and switching resources.  This code attempts
443    // to avoid drawing the incorrect layout while things are in transition.  The method can just
444    // be removed once the race condition is fixed. See bugs 2262578 and 2292713.
445    @Override
446    protected void dispatchDraw(Canvas canvas) {
447        if (DEBUG) Log.v(TAG, "*** dispatchDraw() time: " + SystemClock.elapsedRealtime());
448        super.dispatchDraw(canvas);
449    }
450
451    @Override
452    public void reset() {
453        mIsVerifyUnlockOnly = false;
454        mForgotPattern = false;
455        updateScreen(getInitialMode(), false);
456    }
457
458    @Override
459    public void onScreenTurnedOff() {
460        mScreenOn = false;
461        mForgotPattern = false;
462        if (mMode == Mode.LockScreen) {
463            ((KeyguardScreen) mLockScreen).onPause();
464        } else {
465            ((KeyguardScreen) mUnlockScreen).onPause();
466        }
467    }
468
469    @Override
470    public void onScreenTurnedOn() {
471        mScreenOn = true;
472        if (mMode == Mode.LockScreen) {
473            ((KeyguardScreen) mLockScreen).onResume();
474        } else {
475            ((KeyguardScreen) mUnlockScreen).onResume();
476        }
477    }
478
479    private void recreateLockScreen() {
480        if (mLockScreen != null) {
481            if (mLockScreen.getVisibility() == View.VISIBLE) {
482                ((KeyguardScreen) mLockScreen).onPause();
483            }
484            ((KeyguardScreen) mLockScreen).cleanUp();
485            removeView(mLockScreen);
486        }
487
488        mLockScreen = createLockScreen();
489        mLockScreen.setVisibility(View.INVISIBLE);
490        addView(mLockScreen);
491    }
492
493    private void recreateUnlockScreen(UnlockMode unlockMode) {
494        if (mUnlockScreen != null) {
495            if (mUnlockScreen.getVisibility() == View.VISIBLE) {
496                ((KeyguardScreen) mUnlockScreen).onPause();
497            }
498            ((KeyguardScreen) mUnlockScreen).cleanUp();
499            removeView(mUnlockScreen);
500        }
501
502        mUnlockScreen = createUnlockScreenFor(unlockMode);
503        mUnlockScreen.setVisibility(View.INVISIBLE);
504        addView(mUnlockScreen);
505    }
506
507    @Override
508    protected void onDetachedFromWindow() {
509        removeCallbacks(mRecreateRunnable);
510        super.onDetachedFromWindow();
511    }
512
513    protected void onConfigurationChanged(Configuration newConfig) {
514        Resources resources = getResources();
515        mShowLockBeforeUnlock = resources.getBoolean(R.bool.config_enableLockBeforeUnlockScreen);
516        mConfiguration = newConfig;
517        if (DEBUG_CONFIGURATION) Log.v(TAG, "**** re-creating lock screen since config changed");
518        updateScreen(mMode, true /* force */);
519    }
520
521    @Override
522    protected boolean dispatchHoverEvent(MotionEvent event) {
523        // Do not let the screen to get locked while the user is disabled and touch
524        // exploring. A blind user will need significantly more time to find and
525        // interact with the lock screen views.
526        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
527        if (accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled()) {
528            getCallback().pokeWakelock();
529        }
530        return super.dispatchHoverEvent(event);
531    }
532
533    @Override
534    public void wakeWhenReadyTq(int keyCode) {
535        if (DEBUG) Log.d(TAG, "onWakeKey");
536        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen)
537                && (mUpdateMonitor.getSimState() != IccCard.State.PUK_REQUIRED)) {
538            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
539            updateScreen(Mode.UnlockScreen, false);
540            getCallback().pokeWakelock();
541        } else {
542            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
543            getCallback().pokeWakelock();
544        }
545    }
546
547    @Override
548    public void verifyUnlock() {
549        if (!isSecure()) {
550            // non-secure keyguard screens are successfull by default
551            getCallback().keyguardDone(true);
552        } else if (mUnlockScreenMode != UnlockMode.Pattern
553                && mUnlockScreenMode != UnlockMode.Password) {
554            // can only verify unlock when in pattern/password mode
555            getCallback().keyguardDone(false);
556        } else {
557            // otherwise, go to the unlock screen, see if they can verify it
558            mIsVerifyUnlockOnly = true;
559            updateScreen(Mode.UnlockScreen, false);
560        }
561    }
562
563    @Override
564    public void cleanUp() {
565        if (mLockScreen != null) {
566            ((KeyguardScreen) mLockScreen).onPause();
567            ((KeyguardScreen) mLockScreen).cleanUp();
568            this.removeView(mLockScreen);
569            mLockScreen = null;
570        }
571        if (mUnlockScreen != null) {
572            ((KeyguardScreen) mUnlockScreen).onPause();
573            ((KeyguardScreen) mUnlockScreen).cleanUp();
574            this.removeView(mUnlockScreen);
575            mUnlockScreen = null;
576        }
577    }
578
579    private boolean isSecure() {
580        UnlockMode unlockMode = getUnlockMode();
581        boolean secure = false;
582        switch (unlockMode) {
583            case Pattern:
584                secure = mLockPatternUtils.isLockPatternEnabled();
585                break;
586            case SimPin:
587                secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED;
588                break;
589            case SimPuk:
590                secure = mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
591                break;
592            case Account:
593                secure = true;
594                break;
595            case Password:
596                secure = mLockPatternUtils.isLockPasswordEnabled();
597                break;
598            default:
599                throw new IllegalStateException("unknown unlock mode " + unlockMode);
600        }
601        return secure;
602    }
603
604    private void updateScreen(Mode mode, boolean force) {
605
606        if (DEBUG_CONFIGURATION) Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode
607                + " last mode=" + mMode, new RuntimeException());
608
609        mMode = mode;
610
611        // Re-create the lock screen if necessary
612        if (mode == Mode.LockScreen || mShowLockBeforeUnlock) {
613            if (force || mLockScreen == null) {
614                recreateLockScreen();
615            }
616        }
617
618        // Re-create the unlock screen if necessary. This is primarily required to properly handle
619        // SIM state changes. This typically happens when this method is called by reset()
620        if (mode == Mode.UnlockScreen) {
621            final UnlockMode unlockMode = getUnlockMode();
622            if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) {
623                recreateUnlockScreen(unlockMode);
624            }
625        }
626
627        // visibleScreen should never be null
628        final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen;
629        final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen;
630
631        // do this before changing visibility so focus isn't requested before the input
632        // flag is set
633        mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput());
634
635        if (DEBUG_CONFIGURATION) {
636            Log.v(TAG, "Gone=" + goneScreen);
637            Log.v(TAG, "Visible=" + visibleScreen);
638        }
639
640        if (mScreenOn) {
641            if (goneScreen != null && goneScreen.getVisibility() == View.VISIBLE) {
642                ((KeyguardScreen) goneScreen).onPause();
643            }
644            if (visibleScreen.getVisibility() != View.VISIBLE) {
645                ((KeyguardScreen) visibleScreen).onResume();
646            }
647        }
648
649        if (goneScreen != null) {
650            goneScreen.setVisibility(View.GONE);
651        }
652        visibleScreen.setVisibility(View.VISIBLE);
653        requestLayout();
654
655        if (!visibleScreen.requestFocus()) {
656            throw new IllegalStateException("keyguard screen must be able to take "
657                    + "focus when shown " + visibleScreen.getClass().getCanonicalName());
658        }
659    }
660
661    View createLockScreen() {
662        View lockView = new LockScreen(
663                mContext,
664                mConfiguration,
665                mLockPatternUtils,
666                mUpdateMonitor,
667                mKeyguardScreenCallback);
668        initializeTransportControlView(lockView);
669        return lockView;
670    }
671
672    View createUnlockScreenFor(UnlockMode unlockMode) {
673        View unlockView = null;
674
675        if (DEBUG) Log.d(TAG,
676                "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
677
678        if (unlockMode == UnlockMode.Pattern) {
679            PatternUnlockScreen view = new PatternUnlockScreen(
680                    mContext,
681                    mConfiguration,
682                    mLockPatternUtils,
683                    mUpdateMonitor,
684                    mKeyguardScreenCallback,
685                    mUpdateMonitor.getFailedAttempts());
686            view.setEnableFallback(mEnableFallback);
687            unlockView = view;
688        } else if (unlockMode == UnlockMode.SimPuk) {
689            unlockView = new SimPukUnlockScreen(
690                    mContext,
691                    mConfiguration,
692                    mUpdateMonitor,
693                    mKeyguardScreenCallback,
694                    mLockPatternUtils);
695        } else if (unlockMode == UnlockMode.SimPin) {
696            unlockView = new SimUnlockScreen(
697                    mContext,
698                    mConfiguration,
699                    mUpdateMonitor,
700                    mKeyguardScreenCallback,
701                    mLockPatternUtils);
702        } else if (unlockMode == UnlockMode.Account) {
703            try {
704                unlockView = new AccountUnlockScreen(
705                        mContext,
706                        mConfiguration,
707                        mUpdateMonitor,
708                        mKeyguardScreenCallback,
709                        mLockPatternUtils);
710            } catch (IllegalStateException e) {
711                Log.i(TAG, "Couldn't instantiate AccountUnlockScreen"
712                      + " (IAccountsService isn't available)");
713                // TODO: Need a more general way to provide a
714                // platform-specific fallback UI here.
715                // For now, if we can't display the account login
716                // unlock UI, just bring back the regular "Pattern" unlock mode.
717
718                // (We do this by simply returning a regular UnlockScreen
719                // here.  This means that the user will still see the
720                // regular pattern unlock UI, regardless of the value of
721                // mUnlockScreenMode or whether or not we're in the
722                // "permanently locked" state.)
723                return createUnlockScreenFor(UnlockMode.Pattern);
724            }
725        } else if (unlockMode == UnlockMode.Password) {
726            unlockView = new PasswordUnlockScreen(
727                    mContext,
728                    mConfiguration,
729                    mLockPatternUtils,
730                    mUpdateMonitor,
731                    mKeyguardScreenCallback);
732        } else {
733            throw new IllegalArgumentException("unknown unlock mode " + unlockMode);
734        }
735        initializeTransportControlView(unlockView);
736        mUnlockScreenMode = unlockMode;
737        return unlockView;
738    }
739
740    private void initializeTransportControlView(View view) {
741        com.android.internal.widget.TransportControlView tcv =
742                (TransportControlView) view.findViewById(R.id.transport);
743        if (tcv == null) {
744            if (DEBUG) Log.w(TAG, "Couldn't find transport control widget");
745        } else {
746            tcv.setVisibility(View.GONE); // hide tcv until we get the callback below to show it.
747            tcv.setCallback(mWidgetCallback);
748        }
749    }
750
751    /**
752     * Given the current state of things, what should be the initial mode of
753     * the lock screen (lock or unlock).
754     */
755    private Mode getInitialMode() {
756        final IccCard.State simState = mUpdateMonitor.getSimState();
757        if (stuckOnLockScreenBecauseSimMissing() ||
758                (simState == IccCard.State.PUK_REQUIRED &&
759                        !mLockPatternUtils.isPukUnlockScreenEnable())) {
760            return Mode.LockScreen;
761        } else {
762            if (!isSecure() || mShowLockBeforeUnlock) {
763                return Mode.LockScreen;
764            } else {
765                return Mode.UnlockScreen;
766            }
767        }
768    }
769
770    /**
771     * Given the current state of things, what should the unlock screen be?
772     */
773    private UnlockMode getUnlockMode() {
774        final IccCard.State simState = mUpdateMonitor.getSimState();
775        UnlockMode currentMode;
776        if (simState == IccCard.State.PIN_REQUIRED) {
777            currentMode = UnlockMode.SimPin;
778        } else if (simState == IccCard.State.PUK_REQUIRED) {
779            currentMode = UnlockMode.SimPuk;
780        } else {
781            final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality();
782            switch (mode) {
783                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
784                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
785                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
786                case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
787                    currentMode = UnlockMode.Password;
788                    break;
789                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
790                case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
791                    // "forgot pattern" button is only available in the pattern mode...
792                    if (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) {
793                        currentMode = UnlockMode.Account;
794                    } else {
795                        currentMode = UnlockMode.Pattern;
796                    }
797                    break;
798                default:
799                   throw new IllegalStateException("Unknown unlock mode:" + mode);
800            }
801        }
802        return currentMode;
803    }
804
805    private void showDialog(String title, String message) {
806        final AlertDialog dialog = new AlertDialog.Builder(mContext)
807            .setTitle(title)
808            .setMessage(message)
809            .setNeutralButton(R.string.ok, null)
810            .create();
811        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
812        dialog.show();
813    }
814
815    private void showTimeoutDialog() {
816        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
817        int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message;
818        if (getUnlockMode() == UnlockMode.Password) {
819            if(mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
820                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
821                messageId = R.string.lockscreen_too_many_failed_pin_attempts_dialog_message;
822            } else {
823                messageId = R.string.lockscreen_too_many_failed_password_attempts_dialog_message;
824            }
825        }
826        String message = mContext.getString(messageId, mUpdateMonitor.getFailedAttempts(),
827                timeoutInSeconds);
828
829        showDialog(null, message);
830    }
831
832    private void showAlmostAtAccountLoginDialog() {
833        final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
834        final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
835                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
836        String message = mContext.getString(R.string.lockscreen_failed_attempts_almost_glogin,
837                count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
838        showDialog(null, message);
839    }
840
841    private void showAlmostAtWipeDialog(int attempts, int remaining) {
842        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
843        String message = mContext.getString(
844                R.string.lockscreen_failed_attempts_almost_at_wipe, attempts, remaining);
845        showDialog(null, message);
846    }
847
848    private void showWipeDialog(int attempts) {
849        String message = mContext.getString(
850                R.string.lockscreen_failed_attempts_now_wiping, attempts);
851        showDialog(null, message);
852    }
853
854    /**
855     * Used to put wallpaper on the background of the lock screen.  Centers it
856     * Horizontally and pins the bottom (assuming that the lock screen is aligned
857     * with the bottom, so the wallpaper should extend above the top into the
858     * status bar).
859     */
860    static private class FastBitmapDrawable extends Drawable {
861        private Bitmap mBitmap;
862        private int mOpacity;
863
864        private FastBitmapDrawable(Bitmap bitmap) {
865            mBitmap = bitmap;
866            mOpacity = mBitmap.hasAlpha() ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
867        }
868
869        @Override
870        public void draw(Canvas canvas) {
871            canvas.drawBitmap(
872                    mBitmap,
873                    (getBounds().width() - mBitmap.getWidth()) / 2,
874                    (getBounds().height() - mBitmap.getHeight()),
875                    null);
876        }
877
878        @Override
879        public int getOpacity() {
880            return mOpacity;
881        }
882
883        @Override
884        public void setAlpha(int alpha) {
885        }
886
887        @Override
888        public void setColorFilter(ColorFilter cf) {
889        }
890
891        @Override
892        public int getIntrinsicWidth() {
893            return mBitmap.getWidth();
894        }
895
896        @Override
897        public int getIntrinsicHeight() {
898            return mBitmap.getHeight();
899        }
900
901        @Override
902        public int getMinimumWidth() {
903            return mBitmap.getWidth();
904        }
905
906        @Override
907        public int getMinimumHeight() {
908            return mBitmap.getHeight();
909        }
910    }
911}
912
913