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