LockPatternKeyguardView.java revision cec857d85c838ed7253c64b84d5b1354be7595b1
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.policy.IFaceLockCallback;
22import com.android.internal.policy.IFaceLockInterface;
23import com.android.internal.telephony.IccCard;
24import com.android.internal.widget.LockPatternUtils;
25import com.android.internal.widget.LockScreenWidgetCallback;
26import com.android.internal.widget.LockScreenWidgetInterface;
27import com.android.internal.widget.TransportControlView;
28
29import android.accounts.Account;
30import android.accounts.AccountManager;
31import android.accounts.AccountManagerCallback;
32import android.accounts.AccountManagerFuture;
33import android.accounts.AuthenticatorException;
34import android.accounts.OperationCanceledException;
35import android.app.AlertDialog;
36import android.app.admin.DevicePolicyManager;
37import android.content.ComponentName;
38import android.content.Context;
39import android.content.Intent;
40import android.content.res.Configuration;
41import android.content.res.Resources;
42import android.content.ServiceConnection;
43import android.graphics.Bitmap;
44import android.graphics.Canvas;
45import android.graphics.ColorFilter;
46import android.graphics.PixelFormat;
47import android.graphics.drawable.Drawable;
48import android.os.Bundle;
49import android.os.Handler;
50import android.os.Message;
51import android.os.IBinder;
52import android.os.RemoteException;
53import android.os.SystemClock;
54import android.os.SystemProperties;
55import android.telephony.TelephonyManager;
56import android.text.TextUtils;
57import android.util.Log;
58import android.util.Slog;
59import android.view.KeyEvent;
60import android.view.MotionEvent;
61import android.view.View;
62import android.view.WindowManager;
63import android.view.accessibility.AccessibilityManager;
64
65import java.io.IOException;
66
67
68/**
69 * The host view for all of the screens of the pattern unlock screen.  There are
70 * two {@link Mode}s of operation, lock and unlock.  This will show the appropriate
71 * screen, and listen for callbacks via
72 * {@link com.android.internal.policy.impl.KeyguardScreenCallback}
73 * from the current screen.
74 *
75 * This view, in turn, communicates back to
76 * {@link com.android.internal.policy.impl.KeyguardViewManager}
77 * via its {@link com.android.internal.policy.impl.KeyguardViewCallback}, as appropriate.
78 */
79public class LockPatternKeyguardView extends KeyguardViewBase implements Handler.Callback {
80
81    private static final int TRANSPORT_USERACTIVITY_TIMEOUT = 10000;
82
83    static final boolean DEBUG_CONFIGURATION = false;
84
85    // time after launching EmergencyDialer before the screen goes blank.
86    private static final int EMERGENCY_CALL_TIMEOUT = 10000;
87
88    // intent action for launching emergency dialer activity.
89    static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
90
91    private static final boolean DEBUG = false;
92    private static final String TAG = "LockPatternKeyguardView";
93
94    private final KeyguardUpdateMonitor mUpdateMonitor;
95    private final KeyguardWindowController mWindowController;
96
97    private View mLockScreen;
98    private View mUnlockScreen;
99
100    private boolean mScreenOn = false;
101    private boolean mEnableFallback = false; // assume no fallback UI until we know better
102
103    private boolean mShowLockBeforeUnlock = false;
104
105    // The following were added to support FaceLock
106    private IFaceLockInterface mFaceLockService;
107    private boolean mBoundToFaceLockService = false;
108    private View mFaceLockAreaView;
109
110    private boolean mFaceLockServiceRunning = false;
111    private final Object mFaceLockServiceRunningLock = new Object();
112
113    private Handler mHandler;
114    private final int MSG_SHOW_FACELOCK_AREA_VIEW = 0;
115    private final int MSG_HIDE_FACELOCK_AREA_VIEW = 1;
116
117    /**
118     * The current {@link KeyguardScreen} will use this to communicate back to us.
119     */
120    KeyguardScreenCallback mKeyguardScreenCallback;
121
122
123    private boolean mRequiresSim;
124
125
126    /**
127     * Either a lock screen (an informational keyguard screen), or an unlock
128     * screen (a means for unlocking the device) is shown at any given time.
129     */
130    enum Mode {
131        LockScreen,
132        UnlockScreen
133    }
134
135    /**
136     * The different types screens available for {@link Mode#UnlockScreen}.
137     * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode()
138     */
139    enum UnlockMode {
140
141        /**
142         * Unlock by drawing a pattern.
143         */
144        Pattern,
145
146        /**
147         * Unlock by entering a sim pin.
148         */
149        SimPin,
150
151        /**
152         * Unlock by entering a sim puk.
153         */
154        SimPuk,
155
156        /**
157         * Unlock by entering an account's login and password.
158         */
159        Account,
160
161        /**
162         * Unlock by entering a password or PIN
163         */
164        Password,
165
166        /**
167         * Unknown (uninitialized) value
168         */
169        Unknown
170    }
171
172    /**
173     * The current mode.
174     */
175    private Mode mMode = Mode.LockScreen;
176
177    /**
178     * Keeps track of what mode the current unlock screen is (cached from most recent computation in
179     * {@link #getUnlockMode}).
180     */
181    private UnlockMode mUnlockScreenMode = UnlockMode.Unknown;
182
183    private boolean mForgotPattern;
184
185    /**
186     * If true, it means we are in the process of verifying that the user
187     * can get past the lock screen per {@link #verifyUnlock()}
188     */
189    private boolean mIsVerifyUnlockOnly = false;
190
191
192    /**
193     * Used to lookup the state of the lock pattern
194     */
195    private final LockPatternUtils mLockPatternUtils;
196
197    /**
198     * The current configuration.
199     */
200    private Configuration mConfiguration;
201
202    private Runnable mRecreateRunnable = new Runnable() {
203        public void run() {
204            updateScreen(mMode, true);
205        }
206    };
207
208    private LockScreenWidgetCallback mWidgetCallback = new LockScreenWidgetCallback() {
209        public void userActivity(View self) {
210            mKeyguardScreenCallback.pokeWakelock(TRANSPORT_USERACTIVITY_TIMEOUT);
211        }
212
213        public void requestShow(View view) {
214            if (DEBUG) Log.v(TAG, "View " + view + " requested show transports");
215            view.setVisibility(View.VISIBLE);
216
217            // TODO: examine all widgets to derive clock status
218            mUpdateMonitor.reportClockVisible(false);
219        }
220
221        public void requestHide(View view) {
222            if (DEBUG) Log.v(TAG, "View " + view + " requested hide transports");
223            view.setVisibility(View.GONE);
224
225            // TODO: examine all widgets to derive clock status
226            mUpdateMonitor.reportClockVisible(true);
227        }
228    };
229
230    /**
231     * @return Whether we are stuck on the lock screen because the sim is
232     *   missing.
233     */
234    private boolean stuckOnLockScreenBecauseSimMissing() {
235        return mRequiresSim
236                && (!mUpdateMonitor.isDeviceProvisioned())
237                && (mUpdateMonitor.getSimState() == IccCard.State.ABSENT ||
238                    mUpdateMonitor.getSimState() == IccCard.State.PERM_DISABLED);
239    }
240
241    /**
242     * @param context Used to inflate, and create views.
243     * @param updateMonitor Knows the state of the world, and passed along to each
244     *   screen so they can use the knowledge, and also register for callbacks
245     *   on dynamic information.
246     * @param lockPatternUtils Used to look up state of lock pattern.
247     */
248    public LockPatternKeyguardView(
249            Context context,
250            KeyguardUpdateMonitor updateMonitor,
251            LockPatternUtils lockPatternUtils,
252            KeyguardWindowController controller) {
253        super(context);
254
255        mHandler = new Handler(this);
256        mConfiguration = context.getResources().getConfiguration();
257        mEnableFallback = false;
258        mRequiresSim = TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim"));
259        mUpdateMonitor = updateMonitor;
260        mLockPatternUtils = lockPatternUtils;
261        mWindowController = controller;
262
263        mKeyguardScreenCallback = new KeyguardScreenCallback() {
264
265            public void goToLockScreen() {
266                mForgotPattern = false;
267                if (mIsVerifyUnlockOnly) {
268                    // navigating away from unlock screen during verify mode means
269                    // we are done and the user failed to authenticate.
270                    mIsVerifyUnlockOnly = false;
271                    getCallback().keyguardDone(false);
272                } else {
273                    updateScreen(Mode.LockScreen, false);
274                }
275            }
276
277            public void goToUnlockScreen() {
278                final IccCard.State simState = mUpdateMonitor.getSimState();
279                if (stuckOnLockScreenBecauseSimMissing()
280                         || (simState == IccCard.State.PUK_REQUIRED
281                             && !mLockPatternUtils.isPukUnlockScreenEnable())){
282                    // stuck on lock screen when sim missing or
283                    // puk'd but puk unlock screen is disabled
284                    return;
285                }
286                if (!isSecure()) {
287                    getCallback().keyguardDone(true);
288                } else {
289                    updateScreen(Mode.UnlockScreen, false);
290                }
291            }
292
293            public void forgotPattern(boolean isForgotten) {
294                if (mEnableFallback) {
295                    mForgotPattern = isForgotten;
296                    updateScreen(Mode.UnlockScreen, false);
297                }
298            }
299
300            public boolean isSecure() {
301                return LockPatternKeyguardView.this.isSecure();
302            }
303
304            public boolean isVerifyUnlockOnly() {
305                return mIsVerifyUnlockOnly;
306            }
307
308            public void recreateMe(Configuration config) {
309                removeCallbacks(mRecreateRunnable);
310                post(mRecreateRunnable);
311            }
312
313            public void takeEmergencyCallAction() {
314                pokeWakelock(EMERGENCY_CALL_TIMEOUT);
315                if (TelephonyManager.getDefault().getCallState()
316                        == TelephonyManager.CALL_STATE_OFFHOOK) {
317                    mLockPatternUtils.resumeCall();
318                } else {
319                    Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
320                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
321                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
322                    getContext().startActivity(intent);
323                }
324            }
325
326            public void pokeWakelock() {
327                getCallback().pokeWakelock();
328            }
329
330            public void pokeWakelock(int millis) {
331                getCallback().pokeWakelock(millis);
332            }
333
334            public void keyguardDone(boolean authenticated) {
335                getCallback().keyguardDone(authenticated);
336            }
337
338            public void keyguardDoneDrawing() {
339                // irrelevant to keyguard screen, they shouldn't be calling this
340            }
341
342            public void reportFailedUnlockAttempt() {
343                mUpdateMonitor.reportFailedAttempt();
344                final int failedAttempts = mUpdateMonitor.getFailedAttempts();
345                if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts +
346                    " (enableFallback=" + mEnableFallback + ")");
347
348                final boolean usingPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
349                        == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
350
351                final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
352                        .getMaximumFailedPasswordsForWipe(null);
353
354                final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
355                        - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
356
357                final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
358                        (failedAttemptsBeforeWipe - failedAttempts)
359                        : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
360
361                if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
362                    // If we reach this code, it means the user has installed a DevicePolicyManager
363                    // that requests device wipe after N attempts.  Once we get below the grace
364                    // period, we'll post this dialog every time as a clear warning until the
365                    // bombshell hits and the device is wiped.
366                    if (remainingBeforeWipe > 0) {
367                        showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
368                    } else {
369                        // Too many attempts. The device will be wiped shortly.
370                        Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
371                        showWipeDialog(failedAttempts);
372                    }
373                } else if (usingPattern && mEnableFallback) {
374                    if (failedAttempts == failedAttemptWarning) {
375                        showAlmostAtAccountLoginDialog();
376                    } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
377                        mLockPatternUtils.setPermanentlyLocked(true);
378                        updateScreen(mMode, false);
379                    }
380                } else {
381                    final boolean showTimeout =
382                        (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
383                    if (showTimeout) {
384                        showTimeoutDialog();
385                    }
386                }
387                mLockPatternUtils.reportFailedPasswordAttempt();
388            }
389
390            public boolean doesFallbackUnlockScreenExist() {
391                return mEnableFallback;
392            }
393
394            public void reportSuccessfulUnlockAttempt() {
395                mLockPatternUtils.reportSuccessfulPasswordAttempt();
396            }
397        };
398
399        /**
400         * We'll get key events the current screen doesn't use. see
401         * {@link KeyguardViewBase#onKeyDown(int, android.view.KeyEvent)}
402         */
403        setFocusableInTouchMode(true);
404        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
405
406        updateScreen(getInitialMode(), false);
407        maybeEnableFallback(context);
408    }
409
410    private class AccountAnalyzer implements AccountManagerCallback<Bundle> {
411        private final AccountManager mAccountManager;
412        private final Account[] mAccounts;
413        private int mAccountIndex;
414
415        private AccountAnalyzer(AccountManager accountManager) {
416            mAccountManager = accountManager;
417            mAccounts = accountManager.getAccountsByType("com.google");
418        }
419
420        private void next() {
421            // if we are ready to enable the fallback or if we depleted the list of accounts
422            // then finish and get out
423            if (mEnableFallback || mAccountIndex >= mAccounts.length) {
424                if (mUnlockScreen == null) {
425                    if (DEBUG) Log.w(TAG, "no unlock screen when trying to enable fallback");
426                } else if (mUnlockScreen instanceof PatternUnlockScreen) {
427                    ((PatternUnlockScreen)mUnlockScreen).setEnableFallback(mEnableFallback);
428                }
429                return;
430            }
431
432            // lookup the confirmCredentials intent for the current account
433            mAccountManager.confirmCredentials(mAccounts[mAccountIndex], null, null, this, null);
434        }
435
436        public void start() {
437            mEnableFallback = false;
438            mAccountIndex = 0;
439            next();
440        }
441
442        public void run(AccountManagerFuture<Bundle> future) {
443            try {
444                Bundle result = future.getResult();
445                if (result.getParcelable(AccountManager.KEY_INTENT) != null) {
446                    mEnableFallback = true;
447                }
448            } catch (OperationCanceledException e) {
449                // just skip the account if we are unable to query it
450            } catch (IOException e) {
451                // just skip the account if we are unable to query it
452            } catch (AuthenticatorException e) {
453                // just skip the account if we are unable to query it
454            } finally {
455                mAccountIndex++;
456                next();
457            }
458        }
459    }
460
461    private void maybeEnableFallback(Context context) {
462        // Ask the account manager if we have an account that can be used as a
463        // fallback in case the user forgets his pattern.
464        AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context));
465        accountAnalyzer.start();
466    }
467
468
469    // TODO:
470    // This overloaded method was added to workaround a race condition in the framework between
471    // notification for orientation changed, layout() and switching resources.  This code attempts
472    // to avoid drawing the incorrect layout while things are in transition.  The method can just
473    // be removed once the race condition is fixed. See bugs 2262578 and 2292713.
474    @Override
475    protected void dispatchDraw(Canvas canvas) {
476        if (DEBUG) Log.v(TAG, "*** dispatchDraw() time: " + SystemClock.elapsedRealtime());
477        super.dispatchDraw(canvas);
478    }
479
480    @Override
481    public void reset() {
482        mIsVerifyUnlockOnly = false;
483        mForgotPattern = false;
484        post(mRecreateRunnable);
485    }
486
487    @Override
488    public void onScreenTurnedOff() {
489        mScreenOn = false;
490        mForgotPattern = false;
491        if (mMode == Mode.LockScreen) {
492            ((KeyguardScreen) mLockScreen).onPause();
493        } else {
494            ((KeyguardScreen) mUnlockScreen).onPause();
495        }
496    }
497
498    @Override
499    public void onScreenTurnedOn() {
500        mScreenOn = true;
501        if (mMode == Mode.LockScreen) {
502            ((KeyguardScreen) mLockScreen).onResume();
503        } else {
504            ((KeyguardScreen) mUnlockScreen).onResume();
505        }
506    }
507
508    private void recreateLockScreen() {
509        if (mLockScreen != null) {
510            ((KeyguardScreen) mLockScreen).onPause();
511            ((KeyguardScreen) mLockScreen).cleanUp();
512            removeView(mLockScreen);
513        }
514
515        mLockScreen = createLockScreen();
516        mLockScreen.setVisibility(View.INVISIBLE);
517        addView(mLockScreen);
518    }
519
520    private void recreateUnlockScreen(UnlockMode unlockMode) {
521        if (mUnlockScreen != null) {
522            ((KeyguardScreen) mUnlockScreen).onPause();
523            ((KeyguardScreen) mUnlockScreen).cleanUp();
524            removeView(mUnlockScreen);
525        }
526
527        mUnlockScreen = createUnlockScreenFor(unlockMode);
528        mUnlockScreen.setVisibility(View.INVISIBLE);
529        addView(mUnlockScreen);
530    }
531
532    @Override
533    protected void onDetachedFromWindow() {
534        removeCallbacks(mRecreateRunnable);
535        super.onDetachedFromWindow();
536    }
537
538    protected void onConfigurationChanged(Configuration newConfig) {
539        Resources resources = getResources();
540        mShowLockBeforeUnlock = resources.getBoolean(R.bool.config_enableLockBeforeUnlockScreen);
541        mConfiguration = newConfig;
542        if (DEBUG_CONFIGURATION) Log.v(TAG, "**** re-creating lock screen since config changed");
543        removeCallbacks(mRecreateRunnable);
544        post(mRecreateRunnable);
545    }
546
547    @Override
548    protected boolean dispatchHoverEvent(MotionEvent event) {
549        // Do not let the screen to get locked while the user is disabled and touch
550        // exploring. A blind user will need significantly more time to find and
551        // interact with the lock screen views.
552        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
553        if (accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled()) {
554            getCallback().pokeWakelock();
555        }
556        return super.dispatchHoverEvent(event);
557    }
558
559    @Override
560    public void wakeWhenReadyTq(int keyCode) {
561        if (DEBUG) Log.d(TAG, "onWakeKey");
562        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen)
563                && (mUpdateMonitor.getSimState() != IccCard.State.PUK_REQUIRED)) {
564            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
565            updateScreen(Mode.UnlockScreen, false);
566            getCallback().pokeWakelock();
567        } else {
568            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
569            getCallback().pokeWakelock();
570        }
571    }
572
573    @Override
574    public void verifyUnlock() {
575        if (!isSecure()) {
576            // non-secure keyguard screens are successfull by default
577            getCallback().keyguardDone(true);
578        } else if (mUnlockScreenMode != UnlockMode.Pattern
579                && mUnlockScreenMode != UnlockMode.Password) {
580            // can only verify unlock when in pattern/password mode
581            getCallback().keyguardDone(false);
582        } else {
583            // otherwise, go to the unlock screen, see if they can verify it
584            mIsVerifyUnlockOnly = true;
585            updateScreen(Mode.UnlockScreen, false);
586        }
587    }
588
589    @Override
590    public void cleanUp() {
591        if (mLockScreen != null) {
592            ((KeyguardScreen) mLockScreen).onPause();
593            ((KeyguardScreen) mLockScreen).cleanUp();
594            this.removeView(mLockScreen);
595            mLockScreen = null;
596        }
597        if (mUnlockScreen != null) {
598            ((KeyguardScreen) mUnlockScreen).onPause();
599            ((KeyguardScreen) mUnlockScreen).cleanUp();
600            this.removeView(mUnlockScreen);
601            mUnlockScreen = null;
602        }
603    }
604
605    private boolean isSecure() {
606        UnlockMode unlockMode = getUnlockMode();
607        boolean secure = false;
608        switch (unlockMode) {
609            case Pattern:
610                secure = mLockPatternUtils.isLockPatternEnabled();
611                break;
612            case SimPin:
613                secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED;
614                break;
615            case SimPuk:
616                secure = mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
617                break;
618            case Account:
619                secure = true;
620                break;
621            case Password:
622                secure = mLockPatternUtils.isLockPasswordEnabled();
623                break;
624            default:
625                throw new IllegalStateException("unknown unlock mode " + unlockMode);
626        }
627        return secure;
628    }
629
630    private void updateScreen(Mode mode, boolean force) {
631
632        if (DEBUG_CONFIGURATION) Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode
633                + " last mode=" + mMode + ", force = " + force, new RuntimeException());
634
635        mMode = mode;
636
637        // Re-create the lock screen if necessary
638        if (mode == Mode.LockScreen || mShowLockBeforeUnlock) {
639            if (force || mLockScreen == null) {
640                recreateLockScreen();
641            }
642        }
643
644        // Re-create the unlock screen if necessary. This is primarily required to properly handle
645        // SIM state changes. This typically happens when this method is called by reset()
646        if (mode == Mode.UnlockScreen) {
647            final UnlockMode unlockMode = getUnlockMode();
648            if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) {
649                recreateUnlockScreen(unlockMode);
650            }
651        }
652
653        // visibleScreen should never be null
654        final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen;
655        final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen;
656
657        // do this before changing visibility so focus isn't requested before the input
658        // flag is set
659        mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput());
660
661        if (DEBUG_CONFIGURATION) {
662            Log.v(TAG, "Gone=" + goneScreen);
663            Log.v(TAG, "Visible=" + visibleScreen);
664        }
665
666        if (mScreenOn) {
667            if (goneScreen != null && goneScreen.getVisibility() == View.VISIBLE) {
668                ((KeyguardScreen) goneScreen).onPause();
669            }
670            if (visibleScreen.getVisibility() != View.VISIBLE) {
671                ((KeyguardScreen) visibleScreen).onResume();
672            }
673        }
674
675        if (goneScreen != null) {
676            goneScreen.setVisibility(View.GONE);
677        }
678        visibleScreen.setVisibility(View.VISIBLE);
679        requestLayout();
680
681        if (!visibleScreen.requestFocus()) {
682            throw new IllegalStateException("keyguard screen must be able to take "
683                    + "focus when shown " + visibleScreen.getClass().getCanonicalName());
684        }
685    }
686
687    View createLockScreen() {
688        View lockView = new LockScreen(
689                mContext,
690                mConfiguration,
691                mLockPatternUtils,
692                mUpdateMonitor,
693                mKeyguardScreenCallback);
694        initializeTransportControlView(lockView);
695        return lockView;
696    }
697
698    View createUnlockScreenFor(UnlockMode unlockMode) {
699        View unlockView = null;
700
701        if (DEBUG) Log.d(TAG,
702                "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
703
704        if (unlockMode == UnlockMode.Pattern) {
705            PatternUnlockScreen view = new PatternUnlockScreen(
706                    mContext,
707                    mConfiguration,
708                    mLockPatternUtils,
709                    mUpdateMonitor,
710                    mKeyguardScreenCallback,
711                    mUpdateMonitor.getFailedAttempts());
712            view.setEnableFallback(mEnableFallback);
713            unlockView = view;
714        } else if (unlockMode == UnlockMode.SimPuk) {
715            unlockView = new SimPukUnlockScreen(
716                    mContext,
717                    mConfiguration,
718                    mUpdateMonitor,
719                    mKeyguardScreenCallback,
720                    mLockPatternUtils);
721        } else if (unlockMode == UnlockMode.SimPin) {
722            unlockView = new SimUnlockScreen(
723                    mContext,
724                    mConfiguration,
725                    mUpdateMonitor,
726                    mKeyguardScreenCallback,
727                    mLockPatternUtils);
728        } else if (unlockMode == UnlockMode.Account) {
729            try {
730                unlockView = new AccountUnlockScreen(
731                        mContext,
732                        mConfiguration,
733                        mUpdateMonitor,
734                        mKeyguardScreenCallback,
735                        mLockPatternUtils);
736            } catch (IllegalStateException e) {
737                Log.i(TAG, "Couldn't instantiate AccountUnlockScreen"
738                      + " (IAccountsService isn't available)");
739                // TODO: Need a more general way to provide a
740                // platform-specific fallback UI here.
741                // For now, if we can't display the account login
742                // unlock UI, just bring back the regular "Pattern" unlock mode.
743
744                // (We do this by simply returning a regular UnlockScreen
745                // here.  This means that the user will still see the
746                // regular pattern unlock UI, regardless of the value of
747                // mUnlockScreenMode or whether or not we're in the
748                // "permanently locked" state.)
749                return createUnlockScreenFor(UnlockMode.Pattern);
750            }
751        } else if (unlockMode == UnlockMode.Password) {
752            unlockView = new PasswordUnlockScreen(
753                    mContext,
754                    mConfiguration,
755                    mLockPatternUtils,
756                    mUpdateMonitor,
757                    mKeyguardScreenCallback);
758        } else {
759            throw new IllegalArgumentException("unknown unlock mode " + unlockMode);
760        }
761        initializeTransportControlView(unlockView);
762        initializeFaceLockAreaView(unlockView); // Only shows view if FaceLock is enabled
763
764        mUnlockScreenMode = unlockMode;
765        return unlockView;
766    }
767
768    private void initializeTransportControlView(View view) {
769        com.android.internal.widget.TransportControlView tcv =
770                (TransportControlView) view.findViewById(R.id.transport);
771        if (tcv == null) {
772            if (DEBUG) Log.w(TAG, "Couldn't find transport control widget");
773        } else {
774            mUpdateMonitor.reportClockVisible(true);
775            tcv.setVisibility(View.GONE); // hide tcv until we get the callback below to show it.
776            tcv.setCallback(mWidgetCallback);
777        }
778    }
779
780    /**
781     * Given the current state of things, what should be the initial mode of
782     * the lock screen (lock or unlock).
783     */
784    private Mode getInitialMode() {
785        final IccCard.State simState = mUpdateMonitor.getSimState();
786        if (stuckOnLockScreenBecauseSimMissing() ||
787                (simState == IccCard.State.PUK_REQUIRED &&
788                        !mLockPatternUtils.isPukUnlockScreenEnable())) {
789            return Mode.LockScreen;
790        } else {
791            if (!isSecure() || mShowLockBeforeUnlock) {
792                return Mode.LockScreen;
793            } else {
794                return Mode.UnlockScreen;
795            }
796        }
797    }
798
799    /**
800     * Given the current state of things, what should the unlock screen be?
801     */
802    private UnlockMode getUnlockMode() {
803        final IccCard.State simState = mUpdateMonitor.getSimState();
804        UnlockMode currentMode;
805        if (simState == IccCard.State.PIN_REQUIRED) {
806            currentMode = UnlockMode.SimPin;
807        } else if (simState == IccCard.State.PUK_REQUIRED) {
808            currentMode = UnlockMode.SimPuk;
809        } else {
810            final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality();
811            switch (mode) {
812                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
813                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
814                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
815                case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
816                    currentMode = UnlockMode.Password;
817                    break;
818                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
819                case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
820                    // "forgot pattern" button is only available in the pattern mode...
821                    if (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) {
822                        currentMode = UnlockMode.Account;
823                    } else {
824                        currentMode = UnlockMode.Pattern;
825                    }
826                    break;
827                default:
828                   throw new IllegalStateException("Unknown unlock mode:" + mode);
829            }
830        }
831        return currentMode;
832    }
833
834    private void showDialog(String title, String message) {
835        final AlertDialog dialog = new AlertDialog.Builder(mContext)
836            .setTitle(title)
837            .setMessage(message)
838            .setNeutralButton(R.string.ok, null)
839            .create();
840        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
841        dialog.show();
842    }
843
844    private void showTimeoutDialog() {
845        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
846        int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message;
847        if (getUnlockMode() == UnlockMode.Password) {
848            if(mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
849                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
850                messageId = R.string.lockscreen_too_many_failed_pin_attempts_dialog_message;
851            } else {
852                messageId = R.string.lockscreen_too_many_failed_password_attempts_dialog_message;
853            }
854        }
855        String message = mContext.getString(messageId, mUpdateMonitor.getFailedAttempts(),
856                timeoutInSeconds);
857
858        showDialog(null, message);
859    }
860
861    private void showAlmostAtAccountLoginDialog() {
862        final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
863        final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
864                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
865        String message = mContext.getString(R.string.lockscreen_failed_attempts_almost_glogin,
866                count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
867        showDialog(null, message);
868    }
869
870    private void showAlmostAtWipeDialog(int attempts, int remaining) {
871        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
872        String message = mContext.getString(
873                R.string.lockscreen_failed_attempts_almost_at_wipe, attempts, remaining);
874        showDialog(null, message);
875    }
876
877    private void showWipeDialog(int attempts) {
878        String message = mContext.getString(
879                R.string.lockscreen_failed_attempts_now_wiping, attempts);
880        showDialog(null, message);
881    }
882
883    /**
884     * Used to put wallpaper on the background of the lock screen.  Centers it
885     * Horizontally and pins the bottom (assuming that the lock screen is aligned
886     * with the bottom, so the wallpaper should extend above the top into the
887     * status bar).
888     */
889    static private class FastBitmapDrawable extends Drawable {
890        private Bitmap mBitmap;
891        private int mOpacity;
892
893        private FastBitmapDrawable(Bitmap bitmap) {
894            mBitmap = bitmap;
895            mOpacity = mBitmap.hasAlpha() ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
896        }
897
898        @Override
899        public void draw(Canvas canvas) {
900            canvas.drawBitmap(
901                    mBitmap,
902                    (getBounds().width() - mBitmap.getWidth()) / 2,
903                    (getBounds().height() - mBitmap.getHeight()),
904                    null);
905        }
906
907        @Override
908        public int getOpacity() {
909            return mOpacity;
910        }
911
912        @Override
913        public void setAlpha(int alpha) {
914        }
915
916        @Override
917        public void setColorFilter(ColorFilter cf) {
918        }
919
920        @Override
921        public int getIntrinsicWidth() {
922            return mBitmap.getWidth();
923        }
924
925        @Override
926        public int getIntrinsicHeight() {
927            return mBitmap.getHeight();
928        }
929
930        @Override
931        public int getMinimumWidth() {
932            return mBitmap.getWidth();
933        }
934
935        @Override
936        public int getMinimumHeight() {
937            return mBitmap.getHeight();
938        }
939    }
940
941    // Everything below pertains to FaceLock - might want to separate this out
942
943    // Only pattern and pin unlock screens actually have a view for the FaceLock area, so it's not
944    // uncommon for it to not exist.  But if it does exist, we need to make sure it's showing if
945    // FaceLock is enabled, and make sure it's not showing if FaceLock is disabled
946    private void initializeFaceLockAreaView(View view) {
947        mFaceLockAreaView = view.findViewById(R.id.faceLockAreaView);
948        if (mFaceLockAreaView == null) {
949            if (DEBUG) Log.d(TAG, "Layout does not have faceLockAreaView");
950        } else {
951            if (mLockPatternUtils.usingBiometricWeak()) {
952                mHandler.sendEmptyMessage(MSG_SHOW_FACELOCK_AREA_VIEW);
953            } else {
954                mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
955            }
956        }
957    }
958
959    // Handles covering or exposing FaceLock area on the client side when FaceLock starts or stops
960    // This needs to be done in a handler because the call could be coming from a callback from the
961    // FaceLock service that is in a thread that can't modify the UI
962    @Override
963    public boolean handleMessage(Message msg) {
964        switch (msg.what) {
965        case MSG_SHOW_FACELOCK_AREA_VIEW:
966            mFaceLockAreaView.setVisibility(View.VISIBLE);
967            break;
968        case MSG_HIDE_FACELOCK_AREA_VIEW:
969            mFaceLockAreaView.setVisibility(View.GONE);
970            break;
971        default:
972            Log.w(TAG, "Unhandled message");
973            return false;
974        }
975        return true;
976    }
977
978    // Binds to FaceLock service, but does not tell it to start
979    public void bindToFaceLock() {
980        if (mLockPatternUtils.usingBiometricWeak()) {
981            if (!mBoundToFaceLockService) {
982                if (DEBUG) Log.d(TAG, "before bind to FaceLock service");
983                mContext.bindService(new Intent(IFaceLockInterface.class.getName()),
984                        mFaceLockConnection,
985                        Context.BIND_AUTO_CREATE);
986                if (DEBUG) Log.d(TAG, "after bind to FaceLock service");
987                mBoundToFaceLockService = true;
988            } else {
989                // On startup I've seen onScreenTurnedOn() get called twice without
990                // onScreenTurnedOff() being called in between, which can cause this (bcolonna)
991                if (DEBUG) Log.w(TAG, "Attempt to bind to FaceLock when already bound");
992            }
993        }
994    }
995
996    // Tells FaceLock to stop and then unbinds from the FaceLock service
997    public void stopAndUnbindFromFaceLock() {
998        if (mLockPatternUtils.usingBiometricWeak()) {
999            stopFaceLock();
1000
1001            if (mBoundToFaceLockService) {
1002                if (DEBUG) Log.d(TAG, "before unbind from FaceLock service");
1003                mContext.unbindService(mFaceLockConnection);
1004                if (DEBUG) Log.d(TAG, "after unbind from FaceLock service");
1005                mBoundToFaceLockService = false;
1006            } else {
1007                // This could probably happen after the session when someone activates FaceLock
1008                // because it wasn't active when the phone was turned on
1009                if (DEBUG) Log.w(TAG, "Attempt to unbind from FaceLock when not bound");
1010            }
1011        }
1012    }
1013
1014    private ServiceConnection mFaceLockConnection = new ServiceConnection() {
1015        // Completes connection, registers callback and starts FaceLock when service is bound
1016        @Override
1017        public void onServiceConnected(ComponentName className, IBinder iservice) {
1018            mFaceLockService = IFaceLockInterface.Stub.asInterface(iservice);
1019            if (DEBUG) Log.d(TAG, "Connected to FaceLock service");
1020            try {
1021                mFaceLockService.registerCallback(mFaceLockCallback);
1022            } catch (RemoteException e) {
1023                throw new RuntimeException("Remote exception");
1024            }
1025
1026            if (mFaceLockAreaView != null) {
1027                startFaceLock(mFaceLockAreaView.getWindowToken(),
1028                        mFaceLockAreaView.getLeft(), mFaceLockAreaView.getTop(),
1029                        mFaceLockAreaView.getWidth(), mFaceLockAreaView.getHeight());
1030            }
1031        }
1032
1033        // Cleans up if FaceLock service unexpectedly disconnects
1034        @Override
1035        public void onServiceDisconnected(ComponentName className) {
1036            synchronized(mFaceLockServiceRunningLock) {
1037                mFaceLockService = null;
1038                mFaceLockServiceRunning = false;
1039            }
1040            if (DEBUG) Log.w(TAG, "Unexpected disconnect from FaceLock service");
1041        }
1042    };
1043
1044    // Tells the FaceLock service to start displaying its UI and perform recognition
1045    public void startFaceLock(IBinder windowToken, int x, int y, int h, int w)
1046    {
1047        if (mLockPatternUtils.usingBiometricWeak()) {
1048            synchronized (mFaceLockServiceRunningLock) {
1049                if (!mFaceLockServiceRunning) {
1050                    if (DEBUG) Log.d(TAG, "Starting FaceLock");
1051                    try {
1052                        mFaceLockService.startUi(windowToken, x, y, h, w);
1053                    } catch (RemoteException e) {
1054                        throw new RuntimeException("Remote exception");
1055                    }
1056                    mFaceLockServiceRunning = true;
1057                } else {
1058                    if (DEBUG) Log.w(TAG, "startFaceLock() attempted while running");
1059                }
1060            }
1061        }
1062    }
1063
1064    // Tells the FaceLock service to stop displaying its UI and stop recognition
1065    public void stopFaceLock()
1066    {
1067        if (mLockPatternUtils.usingBiometricWeak()) {
1068            // Note that attempting to stop FaceLock when it's not running is not an issue.
1069            // FaceLock can return, which stops it and then we try to stop it when the
1070            // screen is turned off.  That's why we check.
1071            synchronized (mFaceLockServiceRunningLock) {
1072                if (mFaceLockServiceRunning) {
1073                    try {
1074                        if (DEBUG) Log.d(TAG, "Stopping FaceLock");
1075                        mFaceLockService.stopUi();
1076                    } catch (RemoteException e) {
1077                        throw new RuntimeException("Remote exception");
1078                    }
1079                    mFaceLockServiceRunning = false;
1080                }
1081            }
1082        }
1083    }
1084
1085    // Implements the FaceLock service callback interface defined in AIDL
1086    private final IFaceLockCallback mFaceLockCallback = new IFaceLockCallback.Stub() {
1087
1088        // Stops the FaceLock UI and indicates that the phone should be unlocked
1089        @Override
1090        public void unlock() {
1091            if (DEBUG) Log.d(TAG, "FaceLock unlock");
1092            // Note that we don't hide the client FaceLockAreaView because we want to keep the
1093            // lock screen covered while the phone is unlocked
1094
1095            stopFaceLock();
1096            mKeyguardScreenCallback.keyguardDone(true);
1097            mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
1098        }
1099
1100        // Stops the FaceLock UI and exposes the backup method without unlocking
1101        @Override
1102        public void cancel() {
1103            // In this case, either the user has cancelled out, or FaceLock failed to recognize them
1104            if (DEBUG) Log.d(TAG, "FaceLock cancel");
1105            // Here we hide the client FaceLockViewArea to expose the underlying backup method
1106            mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
1107            stopFaceLock();
1108        }
1109
1110        // Stops the FaceLock UI and puts the phone to sleep
1111        @Override
1112        public void sleepDevice() {
1113            // In this case, it appears the phone has been turned on accidentally
1114            if (DEBUG) Log.d(TAG, "FaceLock accidental turn on");
1115            // Here we hide the client FaceLockViewArea to expose the underlying backup method
1116            mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
1117            stopFaceLock();
1118            // TODO(bcolonna): how do we put the phone back to sleep (i.e., turn off the screen)
1119            // TODO(bcolonna): this should be removed once the service is no longer calling it
1120            // because we are just going to let the lockscreen timeout
1121        }
1122    };
1123}
1124