KeyguardBouncer.java revision e2f0f73591d9d81d4b573bd4b412821d632726fe
1/*
2 * Copyright (C) 2014 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.systemui.statusbar.phone;
18
19import android.app.ActivityManager;
20import android.content.Context;
21import android.os.UserHandle;
22import android.os.UserManager;
23import android.util.Slog;
24import android.view.KeyEvent;
25import android.view.LayoutInflater;
26import android.view.View;
27import android.view.ViewGroup;
28import android.view.ViewTreeObserver;
29import android.view.accessibility.AccessibilityEvent;
30
31import com.android.internal.widget.LockPatternUtils;
32import com.android.keyguard.KeyguardHostView;
33import com.android.keyguard.KeyguardSecurityView;
34import com.android.keyguard.KeyguardUpdateMonitor;
35import com.android.keyguard.KeyguardUpdateMonitorCallback;
36import com.android.keyguard.R;
37import com.android.keyguard.ViewMediatorCallback;
38import com.android.systemui.DejankUtils;
39import com.android.systemui.classifier.FalsingManager;
40
41import static com.android.keyguard.KeyguardHostView.OnDismissAction;
42import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
43
44/**
45 * A class which manages the bouncer on the lockscreen.
46 */
47public class KeyguardBouncer {
48
49    final static private String TAG = "KeyguardBouncer";
50
51    protected Context mContext;
52    protected ViewMediatorCallback mCallback;
53    protected LockPatternUtils mLockPatternUtils;
54    protected ViewGroup mContainer;
55    private StatusBarWindowManager mWindowManager;
56    protected KeyguardHostView mKeyguardView;
57    protected ViewGroup mRoot;
58    private boolean mShowingSoon;
59    private int mBouncerPromptReason;
60    private FalsingManager mFalsingManager;
61    private KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
62            new KeyguardUpdateMonitorCallback() {
63                @Override
64                public void onStrongAuthStateChanged(int userId) {
65                    mBouncerPromptReason = mCallback.getBouncerPromptReason();
66                }
67            };
68
69    public KeyguardBouncer(Context context, ViewMediatorCallback callback,
70            LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager,
71            ViewGroup container) {
72        mContext = context;
73        mCallback = callback;
74        mLockPatternUtils = lockPatternUtils;
75        mContainer = container;
76        mWindowManager = windowManager;
77        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
78        mFalsingManager = FalsingManager.getInstance(mContext);
79    }
80
81    public void show(boolean resetSecuritySelection) {
82        final int keyguardUserId = KeyguardUpdateMonitor.getCurrentUser();
83        if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) {
84            // In split system user mode, we never unlock system user.
85            return;
86        }
87        mFalsingManager.onBouncerShown();
88        ensureView();
89        if (resetSecuritySelection) {
90            // showPrimarySecurityScreen() updates the current security method. This is needed in
91            // case we are already showing and the current security method changed.
92            mKeyguardView.showPrimarySecurityScreen();
93        }
94        if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) {
95            return;
96        }
97
98        final int activeUserId = ActivityManager.getCurrentUser();
99        final boolean allowDismissKeyguard =
100                !(UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM)
101                && activeUserId == keyguardUserId;
102        // If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) is
103        // set, this will dismiss the whole Keyguard. Otherwise, show the bouncer.
104        if (allowDismissKeyguard && mKeyguardView.dismiss()) {
105            return;
106        }
107
108        // This condition may indicate an error on Android, so log it.
109        if (!allowDismissKeyguard) {
110            Slog.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + keyguardUserId);
111        }
112
113        mShowingSoon = true;
114
115        // Split up the work over multiple frames.
116        DejankUtils.postAfterTraversal(mShowRunnable);
117    }
118
119    private final Runnable mShowRunnable = new Runnable() {
120        @Override
121        public void run() {
122            mRoot.setVisibility(View.VISIBLE);
123            mKeyguardView.onResume();
124            showPromptReason(mBouncerPromptReason);
125            if (mKeyguardView.getHeight() != 0) {
126                mKeyguardView.startAppearAnimation();
127            } else {
128                mKeyguardView.getViewTreeObserver().addOnPreDrawListener(
129                        new ViewTreeObserver.OnPreDrawListener() {
130                            @Override
131                            public boolean onPreDraw() {
132                                mKeyguardView.getViewTreeObserver().removeOnPreDrawListener(this);
133                                mKeyguardView.startAppearAnimation();
134                                return true;
135                            }
136                        });
137                mKeyguardView.requestLayout();
138            }
139            mShowingSoon = false;
140            mKeyguardView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
141        }
142    };
143
144    /**
145     * Show a string explaining why the security view needs to be solved.
146     *
147     * @param reason a flag indicating which string should be shown, see
148     *               {@link KeyguardSecurityView#PROMPT_REASON_NONE}
149     *               and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}
150     */
151    public void showPromptReason(int reason) {
152        mKeyguardView.showPromptReason(reason);
153    }
154
155    public void showMessage(String message, int color) {
156        mKeyguardView.showMessage(message, color);
157    }
158
159    private void cancelShowRunnable() {
160        DejankUtils.removeCallbacks(mShowRunnable);
161        mShowingSoon = false;
162    }
163
164    public void showWithDismissAction(OnDismissAction r, Runnable cancelAction) {
165        ensureView();
166        mKeyguardView.setOnDismissAction(r, cancelAction);
167        show(false /* resetSecuritySelection */);
168    }
169
170    public void hide(boolean destroyView) {
171        mFalsingManager.onBouncerHidden();
172        cancelShowRunnable();
173        if (mKeyguardView != null) {
174            mKeyguardView.cancelDismissAction();
175            mKeyguardView.cleanUp();
176        }
177        if (destroyView) {
178            removeView();
179        } else if (mRoot != null) {
180            mRoot.setVisibility(View.INVISIBLE);
181        }
182    }
183
184    /**
185     * See {@link StatusBarKeyguardViewManager#startPreHideAnimation}.
186     */
187    public void startPreHideAnimation(Runnable runnable) {
188        if (mKeyguardView != null) {
189            mKeyguardView.startDisappearAnimation(runnable);
190        } else if (runnable != null) {
191            runnable.run();
192        }
193    }
194
195    /**
196     * Reset the state of the view.
197     */
198    public void reset() {
199        cancelShowRunnable();
200        inflateView();
201        mFalsingManager.onBouncerHidden();
202    }
203
204    public void onScreenTurnedOff() {
205        if (mKeyguardView != null && mRoot != null && mRoot.getVisibility() == View.VISIBLE) {
206            mKeyguardView.onPause();
207        }
208    }
209
210    public boolean isShowing() {
211        return mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE);
212    }
213
214    public void prepare() {
215        boolean wasInitialized = mRoot != null;
216        ensureView();
217        if (wasInitialized) {
218            mKeyguardView.showPrimarySecurityScreen();
219        }
220        mBouncerPromptReason = mCallback.getBouncerPromptReason();
221    }
222
223    protected void ensureView() {
224        if (mRoot == null) {
225            inflateView();
226        }
227    }
228
229    protected void inflateView() {
230        removeView();
231        mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);
232        mKeyguardView = (KeyguardHostView) mRoot.findViewById(R.id.keyguard_host_view);
233        mKeyguardView.setLockPatternUtils(mLockPatternUtils);
234        mKeyguardView.setViewMediatorCallback(mCallback);
235        mContainer.addView(mRoot, mContainer.getChildCount());
236        mRoot.setVisibility(View.INVISIBLE);
237        mRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME);
238    }
239
240    protected void removeView() {
241        if (mRoot != null && mRoot.getParent() == mContainer) {
242            mContainer.removeView(mRoot);
243            mRoot = null;
244        }
245    }
246
247    public boolean onBackPressed() {
248        return mKeyguardView != null && mKeyguardView.handleBackKey();
249    }
250
251    /**
252     * @return True if and only if the security method should be shown before showing the
253     * notifications on Keyguard, like SIM PIN/PUK.
254     */
255    public boolean needsFullscreenBouncer() {
256        ensureView();
257        if (mKeyguardView != null) {
258            SecurityMode mode = mKeyguardView.getSecurityMode();
259            return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
260        }
261        return false;
262    }
263
264    /**
265     * Like {@link #needsFullscreenBouncer}, but uses the currently visible security method, which
266     * makes this method much faster.
267     */
268    public boolean isFullscreenBouncer() {
269        if (mKeyguardView != null) {
270            SecurityMode mode = mKeyguardView.getCurrentSecurityMode();
271            return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
272        }
273        return false;
274    }
275
276    /**
277     * WARNING: This method might cause Binder calls.
278     */
279    public boolean isSecure() {
280        return mKeyguardView == null || mKeyguardView.getSecurityMode() != SecurityMode.None;
281    }
282
283    public boolean shouldDismissOnMenuPressed() {
284        return mKeyguardView.shouldEnableMenuKey();
285    }
286
287    public boolean interceptMediaKey(KeyEvent event) {
288        ensureView();
289        return mKeyguardView.interceptMediaKey(event);
290    }
291
292    public void notifyKeyguardAuthenticated(boolean strongAuth) {
293        ensureView();
294        mKeyguardView.finish(strongAuth);
295    }
296}
297