KeyguardHostView.java revision 0a34d9edf02954d83c2861a8c6836865f8fa00fb
1/*
2 * Copyright (C) 2012 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;
18
19import android.app.Activity;
20import android.app.ActivityManager;
21import android.app.ActivityOptions;
22import android.app.AlertDialog;
23import android.app.admin.DevicePolicyManager;
24import android.appwidget.AppWidgetHost;
25import android.appwidget.AppWidgetHostView;
26import android.appwidget.AppWidgetManager;
27import android.appwidget.AppWidgetProviderInfo;
28import android.content.Context;
29import android.content.Intent;
30import android.content.IntentSender;
31import android.content.pm.UserInfo;
32import android.content.res.Resources;
33import android.graphics.Canvas;
34import android.graphics.Rect;
35import android.os.Looper;
36import android.os.UserManager;
37import android.util.AttributeSet;
38import android.util.Log;
39import android.util.Slog;
40import android.view.KeyEvent;
41import android.view.LayoutInflater;
42import android.view.MotionEvent;
43import android.view.View;
44import android.view.ViewGroup;
45import android.view.WindowManager;
46import android.view.animation.AnimationUtils;
47import android.widget.RemoteViews.OnClickHandler;
48import android.widget.ViewFlipper;
49
50import com.android.internal.R;
51import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
52import com.android.internal.widget.LockPatternUtils;
53
54import java.io.File;
55import java.util.List;
56
57public class KeyguardHostView extends KeyguardViewBase {
58    private static final String TAG = "KeyguardViewHost";
59
60    // Use this to debug all of keyguard
61    public static boolean DEBUG;
62
63    // also referenced in SecuritySettings.java
64    static final int APPWIDGET_HOST_ID = 0x4B455947;
65    private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs";
66
67    private AppWidgetHost mAppWidgetHost;
68    private KeyguardWidgetPager mAppWidgetContainer;
69    private ViewFlipper mSecurityViewContainer;
70    private KeyguardSelectorView mKeyguardSelectorView;
71    private KeyguardTransportControlView mTransportControl;
72    private boolean mEnableMenuKey;
73    private boolean mIsVerifyUnlockOnly;
74    private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
75    private SecurityMode mCurrentSecuritySelection = SecurityMode.None;
76
77    protected Runnable mLaunchRunnable;
78
79    protected int mFailedAttempts;
80    private LockPatternUtils mLockPatternUtils;
81
82    private KeyguardSecurityModel mSecurityModel;
83
84    private Rect mTempRect = new Rect();
85
86    /*package*/ interface TransportCallback {
87        void hide();
88        void show();
89    }
90
91    /*package*/ interface UserSwitcherCallback {
92        void hideSecurityView(int duration);
93        void showSecurityView();
94        void showUnlockHint();
95    }
96
97    public KeyguardHostView(Context context) {
98        this(context, null);
99    }
100
101    public KeyguardHostView(Context context, AttributeSet attrs) {
102        super(context, attrs);
103        mLockPatternUtils = new LockPatternUtils(context);
104        mAppWidgetHost = new AppWidgetHost(
105                context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper());
106        mSecurityModel = new KeyguardSecurityModel(context);
107
108        // The following enables the MENU key to work for testing automation
109        mEnableMenuKey = shouldEnableMenuKey();
110        setFocusable(true);
111        setFocusableInTouchMode(true);
112    }
113
114    @Override
115    public boolean onTouchEvent(MotionEvent ev) {
116        boolean result = super.onTouchEvent(ev);
117        mTempRect.set(0, 0, 0, 0);
118        offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect);
119        ev.offsetLocation(mTempRect.left, mTempRect.top);
120        result = mSecurityViewContainer.dispatchTouchEvent(ev) || result;
121        ev.offsetLocation(-mTempRect.left, -mTempRect.top);
122        return result;
123    }
124
125    @Override
126    protected void dispatchDraw(Canvas canvas) {
127        super.dispatchDraw(canvas);
128        if (mViewMediatorCallback != null) {
129            mViewMediatorCallback.keyguardDoneDrawing();
130        }
131    }
132
133    private int getWidgetPosition(int id) {
134        final int children = mAppWidgetContainer.getChildCount();
135        for (int i = 0; i < children; i++) {
136            if (mAppWidgetContainer.getChildAt(i).getId() == id) {
137                return i;
138            }
139        }
140        return -1;
141    }
142
143    @Override
144    protected void onFinishInflate() {
145        mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
146        KeyguardWidgetRegion kgwr = (KeyguardWidgetRegion) findViewById(R.id.kg_widget_region);
147        kgwr.setVisibility(VISIBLE);
148        mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper);
149        mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
150
151        addDefaultWidgets();
152        updateSecurityViews();
153        setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
154    }
155
156    private void updateSecurityViews() {
157        int children = mSecurityViewContainer.getChildCount();
158        for (int i = 0; i < children; i++) {
159            updateSecurityView(mSecurityViewContainer.getChildAt(i));
160        }
161    }
162
163    private void updateSecurityView(View view) {
164        if (view instanceof KeyguardSecurityView) {
165            KeyguardSecurityView ksv = (KeyguardSecurityView) view;
166            ksv.setKeyguardCallback(mCallback);
167            ksv.setLockPatternUtils(mLockPatternUtils);
168        } else {
169            Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
170        }
171    }
172
173    void setLockPatternUtils(LockPatternUtils utils) {
174        mSecurityModel.setLockPatternUtils(utils);
175        mLockPatternUtils = utils;
176        updateSecurityViews();
177    }
178
179    @Override
180    protected void onAttachedToWindow() {
181        super.onAttachedToWindow();
182        mAppWidgetHost.startListening();
183        maybePopulateWidgets();
184    }
185
186    @Override
187    protected void onDetachedFromWindow() {
188        super.onDetachedFromWindow();
189        mAppWidgetHost.stopListening();
190    }
191
192    private AppWidgetHost getAppWidgetHost() {
193        return mAppWidgetHost;
194    }
195
196    void addWidget(AppWidgetHostView view) {
197        mAppWidgetContainer.addWidget(view);
198    }
199
200    private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
201
202        public void userActivity(long timeout) {
203            if (mViewMediatorCallback != null) {
204                mViewMediatorCallback.userActivity(timeout);
205            }
206        }
207
208        public void dismiss(boolean authenticated) {
209            showNextSecurityScreenOrFinish(authenticated);
210        }
211
212        public boolean isVerifyUnlockOnly() {
213            return mIsVerifyUnlockOnly;
214        }
215
216        public void reportSuccessfulUnlockAttempt() {
217            KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts();
218            mLockPatternUtils.reportSuccessfulPasswordAttempt();
219        }
220
221        public void reportFailedUnlockAttempt() {
222            if (mCurrentSecuritySelection == SecurityMode.Biometric) {
223                KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt();
224            } else {
225                KeyguardHostView.this.reportFailedUnlockAttempt();
226            }
227        }
228
229        public int getFailedAttempts() {
230            return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
231        }
232
233        @Override
234        public void showBackupSecurity() {
235            KeyguardHostView.this.showBackupSecurity();
236        }
237
238        @Override
239        public void setOnDismissRunnable(Runnable runnable) {
240            KeyguardHostView.this.setOnDismissRunnable(runnable);
241        }
242
243    };
244
245    private void showDialog(String title, String message) {
246        final AlertDialog dialog = new AlertDialog.Builder(mContext)
247            .setTitle(title)
248            .setMessage(message)
249            .setNeutralButton(com.android.internal.R.string.ok, null)
250            .create();
251        if (!(mContext instanceof Activity)) {
252            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
253        }
254        dialog.show();
255    }
256
257    private void showTimeoutDialog() {
258        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
259        int messageId = 0;
260
261        switch (mSecurityModel.getSecurityMode()) {
262            case Pattern:
263                messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
264                break;
265
266            case Password: {
267                    final boolean isPin = mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
268                        DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
269                    messageId = isPin ? R.string.kg_too_many_failed_pin_attempts_dialog_message
270                            : R.string.kg_too_many_failed_password_attempts_dialog_message;
271                }
272                break;
273        }
274
275        if (messageId != 0) {
276            final String message = mContext.getString(messageId,
277                    KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
278                    timeoutInSeconds);
279            showDialog(null, message);
280        }
281    }
282
283    private void showAlmostAtWipeDialog(int attempts, int remaining) {
284        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
285        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
286                attempts, remaining);
287        showDialog(null, message);
288    }
289
290    private void showWipeDialog(int attempts) {
291        String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts);
292        showDialog(null, message);
293    }
294
295    private void showAlmostAtAccountLoginDialog() {
296        final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
297        final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
298                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
299        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
300                count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
301        showDialog(null, message);
302    }
303
304    private void reportFailedUnlockAttempt() {
305        final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
306        final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
307
308        if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
309
310        SecurityMode mode = mSecurityModel.getSecurityMode();
311        final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
312
313        final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
314                .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser());
315
316        final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
317                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
318
319        final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
320                (failedAttemptsBeforeWipe - failedAttempts)
321                : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
322
323        boolean showTimeout = false;
324        if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
325            // If we reach this code, it means the user has installed a DevicePolicyManager
326            // that requests device wipe after N attempts.  Once we get below the grace
327            // period, we'll post this dialog every time as a clear warning until the
328            // bombshell hits and the device is wiped.
329            if (remainingBeforeWipe > 0) {
330                showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
331            } else {
332                // Too many attempts. The device will be wiped shortly.
333                Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
334                showWipeDialog(failedAttempts);
335            }
336        } else {
337            showTimeout =
338                (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
339            if (usingPattern && mEnableFallback) {
340                if (failedAttempts == failedAttemptWarning) {
341                    showAlmostAtAccountLoginDialog();
342                    showTimeout = false; // don't show both dialogs
343                } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
344                    mLockPatternUtils.setPermanentlyLocked(true);
345                    showSecurityScreen(SecurityMode.Account);
346                    // don't show timeout dialog because we show account unlock screen next
347                    showTimeout = false;
348                }
349            }
350        }
351        monitor.reportFailedUnlockAttempt();
352        mLockPatternUtils.reportFailedPasswordAttempt();
353        if (showTimeout) {
354            showTimeoutDialog();
355        }
356    }
357
358    /**
359     * Shows the backup security screen for the current security mode.  This could be used for
360     * password recovery screens but is currently only used for pattern unlock to show the
361     * account unlock screen and biometric unlock to show the user's normal unlock.
362     */
363    private void showBackupSecurity() {
364        SecurityMode currentMode = mSecurityModel.getAlternateFor(mSecurityModel.getSecurityMode());
365        showSecurityScreen(mSecurityModel.getBackupFor(currentMode));
366    }
367
368    public boolean showNextSecurityScreenIfPresent() {
369        SecurityMode securityMode = mSecurityModel.getSecurityMode();
370        // Allow an alternate, such as biometric unlock
371        securityMode = mSecurityModel.getAlternateFor(securityMode);
372        if (SecurityMode.None == securityMode) {
373            return false;
374        } else {
375            showSecurityScreen(securityMode); // switch to the alternate security view
376            return true;
377        }
378    }
379
380    private void showNextSecurityScreenOrFinish(boolean authenticated) {
381        boolean finish = false;
382        if (SecurityMode.None == mCurrentSecuritySelection) {
383            SecurityMode securityMode = mSecurityModel.getSecurityMode();
384            // Allow an alternate, such as biometric unlock
385            securityMode = mSecurityModel.getAlternateFor(securityMode);
386            if (SecurityMode.None == securityMode) {
387                finish = true; // no security required
388            } else {
389                showSecurityScreen(securityMode); // switch to the alternate security view
390            }
391        } else if (authenticated) {
392            switch (mCurrentSecuritySelection) {
393                case Pattern:
394                case Password:
395                case Account:
396                case Biometric:
397                    finish = true;
398                    break;
399
400                case SimPin:
401                case SimPuk:
402                    // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
403                    SecurityMode securityMode = mSecurityModel.getSecurityMode();
404                    if (securityMode != SecurityMode.None) {
405                        showSecurityScreen(securityMode);
406                    } else {
407                        finish = true;
408                    }
409                    break;
410
411                default:
412                    showSecurityScreen(SecurityMode.None);
413                    break;
414            }
415        } else {
416            // Not authenticated but we were asked to dismiss so go back to selector screen.
417            showSecurityScreen(SecurityMode.None);
418        }
419        if (finish) {
420            // If there's a pending runnable because the user interacted with a widget
421            // and we're leaving keyguard, then run it.
422            if (mLaunchRunnable != null) {
423                mLaunchRunnable.run();
424                mLaunchRunnable = null;
425            }
426            if (mViewMediatorCallback != null) {
427                mViewMediatorCallback.keyguardDone(true);
428            }
429        }
430    }
431
432    private OnClickHandler mOnClickHandler = new OnClickHandler() {
433        @Override
434        public boolean onClickHandler(final View view,
435                final android.app.PendingIntent pendingIntent,
436                final Intent fillInIntent) {
437            if (pendingIntent.isActivity()) {
438                setOnDismissRunnable(new Runnable() {
439                    public void run() {
440                        try {
441                              // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
442                              Context context = view.getContext();
443                              ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
444                                      0, 0,
445                                      view.getMeasuredWidth(), view.getMeasuredHeight());
446                              context.startIntentSender(
447                                      pendingIntent.getIntentSender(), fillInIntent,
448                                      Intent.FLAG_ACTIVITY_NEW_TASK,
449                                      Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
450                          } catch (IntentSender.SendIntentException e) {
451                              android.util.Log.e(TAG, "Cannot send pending intent: ", e);
452                          } catch (Exception e) {
453                              android.util.Log.e(TAG, "Cannot send pending intent due to " +
454                                      "unknown exception: ", e);
455                          }
456                    }
457                });
458
459                mCallback.dismiss(false);
460                return true;
461            } else {
462                return super.onClickHandler(view, pendingIntent, fillInIntent);
463            }
464        };
465    };
466
467    @Override
468    public void reset() {
469        mIsVerifyUnlockOnly = false;
470        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
471        requestFocus();
472    }
473
474    /**
475     *  Sets a runnable to run when keyguard is dismissed
476     * @param runnable
477     */
478    protected void setOnDismissRunnable(Runnable runnable) {
479        mLaunchRunnable = runnable;
480    }
481
482    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
483        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
484        KeyguardSecurityView view = null;
485        final int children = mSecurityViewContainer.getChildCount();
486        for (int child = 0; child < children; child++) {
487            if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
488                view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
489                break;
490            }
491        }
492        if (view == null) {
493            final LayoutInflater inflater = LayoutInflater.from(mContext);
494            View v = inflater.inflate(getLayoutIdFor(securityMode), this, false);
495            mSecurityViewContainer.addView(v);
496            updateSecurityView(v);
497            view = (KeyguardSecurityView) v;
498        }
499        return view;
500    }
501
502    /**
503     * Switches to the given security view unless it's already being shown, in which case
504     * this is a no-op.
505     *
506     * @param securityMode
507     */
508    private void showSecurityScreen(SecurityMode securityMode) {
509
510        if (securityMode == mCurrentSecuritySelection) return;
511
512        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
513        KeyguardSecurityView newView = getSecurityView(securityMode);
514
515        // Emulate Activity life cycle
516        oldView.onPause();
517        newView.onResume();
518
519        final boolean needsInput = newView.needsInput();
520        if (mViewMediatorCallback != null) {
521            mViewMediatorCallback.setNeedsInput(needsInput);
522        }
523
524        // Find and show this child.
525        final int childCount = mSecurityViewContainer.getChildCount();
526
527        // If we're go to/from the selector view, do flip animation, otherwise use fade animation.
528        final boolean doFlip = mCurrentSecuritySelection == SecurityMode.None
529                || securityMode == SecurityMode.None;
530        final int inAnimation = doFlip ? R.anim.keyguard_security_animate_in
531                : R.anim.keyguard_security_fade_in;
532        final int outAnimation = doFlip ? R.anim.keyguard_security_animate_out
533                : R.anim.keyguard_security_fade_out;
534
535        mSecurityViewContainer.setInAnimation(AnimationUtils.loadAnimation(mContext, inAnimation));
536        mSecurityViewContainer.setOutAnimation(AnimationUtils.loadAnimation(mContext, outAnimation));
537        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
538        for (int i = 0; i < childCount; i++) {
539            if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
540                mSecurityViewContainer.setDisplayedChild(i);
541                break;
542            }
543        }
544
545
546        if (securityMode == SecurityMode.None) {
547            // Discard current runnable if we're switching back to the selector view
548            setOnDismissRunnable(null);
549            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
550        } else {
551            setSystemUiVisibility(getSystemUiVisibility() & (~View.STATUS_BAR_DISABLE_BACK));
552        }
553
554        mCurrentSecuritySelection = securityMode;
555    }
556
557    @Override
558    public void onScreenTurnedOn() {
559        if (DEBUG) Log.d(TAG, "screen on");
560        showSecurityScreen(mCurrentSecuritySelection);
561        getSecurityView(mCurrentSecuritySelection).onResume();
562
563        // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
564        // layout is blank but forcing a layout causes it to reappear (e.g. with with
565        // hierarchyviewer).
566        requestLayout();
567    }
568
569    @Override
570    public void onScreenTurnedOff() {
571        if (DEBUG) Log.d(TAG, "screen off");
572        showSecurityScreen(SecurityMode.None);
573        getSecurityView(mCurrentSecuritySelection).onPause();
574    }
575
576    @Override
577    public void show() {
578        onScreenTurnedOn();
579    }
580
581    private boolean isSecure() {
582        SecurityMode mode = mSecurityModel.getSecurityMode();
583        switch (mode) {
584            case Pattern:
585                return mLockPatternUtils.isLockPatternEnabled();
586            case Password:
587                return mLockPatternUtils.isLockPasswordEnabled();
588            case SimPin:
589            case SimPuk:
590            case Account:
591                return true;
592            case None:
593                return false;
594            default:
595                throw new IllegalStateException("Unknown security mode " + mode);
596        }
597    }
598
599    @Override
600    public void wakeWhenReadyTq(int keyCode) {
601        if (DEBUG) Log.d(TAG, "onWakeKey");
602        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) {
603            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
604            showSecurityScreen(SecurityMode.None);
605        } else {
606            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
607        }
608        if (mViewMediatorCallback != null) {
609            mViewMediatorCallback.wakeUp();
610        }
611    }
612
613    @Override
614    public void verifyUnlock() {
615        SecurityMode securityMode = mSecurityModel.getSecurityMode();
616        if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
617            if (mViewMediatorCallback != null) {
618                mViewMediatorCallback.keyguardDone(true);
619            }
620        } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
621                && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
622            // can only verify unlock when in pattern/password mode
623            if (mViewMediatorCallback != null) {
624                mViewMediatorCallback.keyguardDone(false);
625            }
626        } else {
627            // otherwise, go to the unlock screen, see if they can verify it
628            mIsVerifyUnlockOnly = true;
629            showSecurityScreen(securityMode);
630        }
631    }
632
633    private int getSecurityViewIdForMode(SecurityMode securityMode) {
634        switch (securityMode) {
635            case None: return R.id.keyguard_selector_view;
636            case Pattern: return R.id.keyguard_pattern_view;
637            case Password: return R.id.keyguard_password_view;
638            case Biometric: return R.id.keyguard_face_unlock_view;
639            case Account: return R.id.keyguard_account_view;
640            case SimPin: return R.id.keyguard_sim_pin_view;
641            case SimPuk: return R.id.keyguard_sim_puk_view;
642        }
643        return 0;
644    }
645
646    private int getLayoutIdFor(SecurityMode securityMode) {
647        switch (securityMode) {
648            case None: return R.layout.keyguard_selector_view;
649            case Pattern: return R.layout.keyguard_pattern_view;
650            case Password: return R.layout.keyguard_password_view;
651            case Biometric: return R.layout.keyguard_face_unlock_view;
652            case Account: return R.layout.keyguard_account_view;
653            case SimPin: return R.layout.keyguard_sim_pin_view;
654            case SimPuk: return R.layout.keyguard_sim_puk_view;
655            default:
656                throw new RuntimeException("No layout for securityMode " + securityMode);
657        }
658    }
659
660    private void addWidget(int appId) {
661        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
662        AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId);
663        if (appWidgetInfo != null) {
664            AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
665            addWidget(view);
666        } else {
667            Log.w(TAG, "AppWidgetInfo was null; not adding widget id " + appId);
668        }
669    }
670
671    private void addDefaultWidgets() {
672        LayoutInflater inflater = LayoutInflater.from(mContext);
673        inflater.inflate(R.layout.keyguard_status_view, mAppWidgetContainer, true);
674        inflater.inflate(R.layout.keyguard_transport_control_view, mAppWidgetContainer, true);
675
676        mTransportControl =
677            (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
678
679
680        // This code manages showing/hiding the transport control. We keep it around and only
681        // add it to the hierarchy if it needs to be present.
682        if (mTransportControl != null) {
683            mTransportControl.setKeyguardCallback(new TransportCallback() {
684                boolean mSticky = false;
685                @Override
686                public void hide() {
687                    int page = getWidgetPosition(R.id.keyguard_transport_control);
688                    if (page != -1 && !mSticky) {
689                        if (page == mAppWidgetContainer.getCurrentPage()) {
690                            // Switch back to clock view if music was showing.
691                            mAppWidgetContainer
692                                .setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
693                        }
694                        mAppWidgetContainer.removeView(mTransportControl);
695                        // XXX keep view attached to hierarchy so we still get show/hide events
696                        // from AudioManager
697                        KeyguardHostView.this.addView(mTransportControl);
698                        mTransportControl.setVisibility(View.GONE);
699                    }
700                }
701
702                @Override
703                public void show() {
704                    if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
705                        KeyguardHostView.this.removeView(mTransportControl);
706                        mAppWidgetContainer.addView(mTransportControl,
707                                getWidgetPosition(R.id.keyguard_status_view) + 1);
708                        mTransportControl.setVisibility(View.VISIBLE);
709                        // Once shown, leave it showing
710                        mSticky = true;
711                    }
712                }
713            });
714        }
715    }
716
717    private void maybePopulateWidgets() {
718        DevicePolicyManager dpm =
719                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
720        if (dpm != null) {
721            final int currentUser = mLockPatternUtils.getCurrentUser();
722            final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
723            if ((disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
724                Log.v(TAG, "Keyguard widgets disabled because of device policy admin");
725                return;
726            }
727        }
728        inflateAndAddUserSelectorWidgetIfNecessary();
729
730        // Add status widget
731        View statusView = null;
732        int statusWidgetId = mLockPatternUtils.getStatusWidget();
733        if (statusWidgetId != -1) {
734            addWidget(statusWidgetId);
735            View newStatusWidget = mAppWidgetContainer.getChildAt(
736                    mAppWidgetContainer.getChildCount() - 1);
737
738            int oldStatusWidgetPosition = getWidgetPosition(R.id.keyguard_status_view);
739            mAppWidgetContainer.removeViewAt(oldStatusWidgetPosition);
740
741            // Re-add new status widget at position of old one
742            mAppWidgetContainer.removeView(newStatusWidget);
743            newStatusWidget.setId(R.id.keyguard_status_view);
744            mAppWidgetContainer.addView(newStatusWidget, oldStatusWidgetPosition);
745            statusView = newStatusWidget;
746        } else {
747            statusView = findViewById(R.id.keyguard_status_view);
748        }
749
750        // Disable all user interaction on status view. This is done to prevent falsing in the
751        // pocket from triggering useractivity and prevents 3rd party replacement widgets
752        // from responding to user interaction while in this position.
753        if (statusView instanceof KeyguardWidgetFrame) {
754            ((KeyguardWidgetFrame) statusView).setDisableUserInteraction(true);
755        }
756
757        // Add user-selected widget
758        final int[] widgets = mLockPatternUtils.getUserDefinedWidgets();
759        for (int i = 0; i < widgets.length; i++) {
760            if (widgets[i] != -1) {
761                addWidget(widgets[i]);
762            }
763        }
764        showAppropriateWidgetPage();
765    }
766
767    private void showAppropriateWidgetPage() {
768        int page = mAppWidgetContainer.indexOfChild(findViewById(R.id.keyguard_status_view));
769        if (mAppWidgetContainer.indexOfChild(mTransportControl) != -1) {
770            page = mAppWidgetContainer.indexOfChild(mTransportControl);
771        }
772        mAppWidgetContainer.setCurrentPage(page);
773    }
774
775    private void inflateAndAddUserSelectorWidgetIfNecessary() {
776        // if there are multiple users, we need to add the multi-user switcher widget to the
777        // keyguard.
778        UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
779        List<UserInfo> users = mUm.getUsers(true);
780
781        if (users.size() > 1) {
782            KeyguardWidgetFrame userSwitcher = (KeyguardWidgetFrame)
783                LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
784                        mAppWidgetContainer, false);
785
786            // add the switcher in the first position
787            mAppWidgetContainer.addView(userSwitcher, getWidgetPosition(R.id.keyguard_status_view));
788            KeyguardMultiUserSelectorView multiUser = (KeyguardMultiUserSelectorView)
789                    userSwitcher.getChildAt(0);
790
791            UserSwitcherCallback callback = new UserSwitcherCallback() {
792                @Override
793                public void hideSecurityView(int duration) {
794                    mSecurityViewContainer.animate().alpha(0).setDuration(duration);
795                }
796
797                @Override
798                public void showSecurityView() {
799                    mSecurityViewContainer.setAlpha(1.0f);
800                }
801
802                @Override
803                public void showUnlockHint() {
804                    mKeyguardSelectorView.ping();
805                }
806
807            };
808            multiUser.setCallback(callback);
809        }
810    }
811
812    @Override
813    public void cleanUp() {
814
815    }
816
817    /**
818     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
819     * some cases where we wish to disable it, notably when the menu button placement or technology
820     * is prone to false positives.
821     *
822     * @return true if the menu key should be enabled
823     */
824    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
825    private boolean shouldEnableMenuKey() {
826        final Resources res = getResources();
827        final boolean configDisabled = res.getBoolean(
828                com.android.internal.R.bool.config_disableMenuKeyInLockScreen);
829        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
830        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
831        return !configDisabled || isTestHarness || fileOverride;
832    }
833
834    @Override
835    public boolean onKeyDown(int keyCode, KeyEvent event) {
836        if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKey) {
837            showNextSecurityScreenOrFinish(false);
838            return true;
839        } else {
840            return super.onKeyDown(keyCode, event);
841        }
842    }
843
844    public void goToUserSwitcher() {
845        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector));
846    }
847
848    public boolean handleBackKey() {
849        if (mCurrentSecuritySelection != SecurityMode.None) {
850            mCallback.dismiss(false);
851            return true;
852        }
853        return false;
854    }
855
856}
857