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