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