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