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