KeyguardController.java revision e69c93181f1f313dcedd07f677af1cea953fdf16
1/*
2 * Copyright (C) 2016 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.server.am;
18
19import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
20import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
21import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
22import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
23import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
24import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
25import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
26import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
27import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
28import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_OCCLUDE;
29import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
30import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
31
32import com.android.server.wm.WindowManagerService;
33
34import java.io.PrintWriter;
35import java.util.ArrayList;
36
37/**
38 * Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
39 * currently visible.
40 * <p>
41 * Note that everything in this class should only be accessed with the AM lock being held.
42 */
43class KeyguardController {
44
45    private final ActivityManagerService mService;
46    private final ActivityStackSupervisor mStackSupervisor;
47    private WindowManagerService mWindowManager;
48    private boolean mKeyguardShowing;
49    private boolean mKeyguardGoingAway;
50    private boolean mOccluded;
51    private ActivityRecord mDismissingKeyguardActivity;
52    private int mBeforeUnoccludeTransit;
53    private int mVisibilityTransactionDepth;
54
55    KeyguardController(ActivityManagerService service,
56            ActivityStackSupervisor stackSupervisor) {
57        mService = service;
58        mStackSupervisor = stackSupervisor;
59    }
60
61    void setWindowManager(WindowManagerService windowManager) {
62        mWindowManager = windowManager;
63    }
64
65    /**
66     * @return true if Keyguard is showing, not going away, and not being occluded, false otherwise
67     */
68    boolean isKeyguardShowing() {
69        return mKeyguardShowing && !mKeyguardGoingAway && !mOccluded;
70    }
71
72    /**
73     * @return true if Keyguard is either showing or occluded, but not going away
74     */
75    boolean isKeyguardLocked() {
76        return mKeyguardShowing && !mKeyguardGoingAway;
77    }
78
79    /**
80     * Update the Keyguard showing state.
81     */
82    void setKeyguardShown(boolean showing) {
83        if (showing == mKeyguardShowing) {
84            return;
85        }
86        mKeyguardShowing = showing;
87        if (showing) {
88            mKeyguardGoingAway = false;
89
90            // Allow an activity to redismiss Keyguard.
91            mDismissingKeyguardActivity = null;
92        }
93        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
94        mService.updateSleepIfNeededLocked();
95    }
96
97    /**
98     * Called when Keyguard is going away.
99     *
100     * @param flags See {@link android.view.WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
101     *              etc.
102     */
103    void keyguardGoingAway(int flags) {
104        if (mKeyguardShowing) {
105            mWindowManager.deferSurfaceLayout();
106            try {
107                mKeyguardGoingAway = true;
108                mWindowManager.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
109                        false /* alwaysKeepCurrent */, convertTransitFlags(flags),
110                        false /* forceOverride */);
111                mWindowManager.keyguardGoingAway(flags);
112                mService.updateSleepIfNeededLocked();
113
114                // Some stack visibility might change (e.g. docked stack)
115                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
116                mWindowManager.executeAppTransition();
117                mService.applyVrModeIfNeededLocked(mStackSupervisor.getResumedActivityLocked(),
118                        true /* enable */);
119            } finally {
120                mWindowManager.continueSurfaceLayout();
121            }
122        }
123    }
124
125    private int convertTransitFlags(int keyguardGoingAwayFlags) {
126        int result = 0;
127        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) {
128            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
129        }
130        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0) {
131            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
132        }
133        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0) {
134            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
135        }
136        return result;
137    }
138
139    /**
140     * Starts a batch of visibility updates.
141     */
142    void beginActivityVisibilityUpdate() {
143        mVisibilityTransactionDepth++;
144    }
145
146    /**
147     * Ends a batch of visibility updates. After all batches are done, this method makes sure to
148     * update lockscreen occluded/dismiss state if needed.
149     */
150    void endActivityVisibilityUpdate() {
151        mVisibilityTransactionDepth--;
152        if (mVisibilityTransactionDepth == 0) {
153            visibilitiesUpdated();
154        }
155    }
156
157    /**
158     * @return True if we may show an activity while Keyguard is showing because we are in the
159     *         process of dismissing it anyways, false otherwise.
160     */
161    boolean canShowActivityWhileKeyguardShowing(boolean dismissKeyguard) {
162        return dismissKeyguard && canDismissKeyguard();
163    }
164
165    private void visibilitiesUpdated() {
166        final boolean lastOccluded = mOccluded;
167        final ActivityRecord lastDismissingKeyguardActivity = mDismissingKeyguardActivity;
168        mOccluded = false;
169        mDismissingKeyguardActivity = null;
170        final ArrayList<ActivityStack> stacks = mStackSupervisor.getStacksOnDefaultDisplay();
171        final int topStackNdx = stacks.size() - 1;
172        for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
173            final ActivityStack stack = stacks.get(stackNdx);
174
175            // Only the very top activity may control occluded state
176            if (stackNdx == topStackNdx) {
177                mOccluded = stack.topActivityOccludesKeyguard();
178            }
179            if (mDismissingKeyguardActivity == null
180                    && stack.getTopDismissingKeyguardActivity() != null) {
181                mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
182            }
183        }
184        mOccluded |= mWindowManager.isShowingDream();
185        if (mOccluded != lastOccluded) {
186            handleOccludedChanged();
187        }
188        if (mDismissingKeyguardActivity != lastDismissingKeyguardActivity) {
189            handleDismissKeyguard();
190        }
191    }
192
193    /**
194     * Called when occluded state changed.
195     */
196    private void handleOccludedChanged() {
197        mWindowManager.onKeyguardOccludedChanged(mOccluded);
198        if (isKeyguardLocked()) {
199            mWindowManager.deferSurfaceLayout();
200            try {
201                mWindowManager.prepareAppTransition(resolveOccludeTransit(),
202                        false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
203                mService.updateSleepIfNeededLocked();
204                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
205                mWindowManager.executeAppTransition();
206            } finally {
207                mWindowManager.continueSurfaceLayout();
208            }
209        }
210        dismissDockedStackIfNeeded();
211    }
212
213    /**
214     * Called when somebody might want to dismiss the Keyguard.
215     */
216    private void handleDismissKeyguard() {
217        if (mDismissingKeyguardActivity != null) {
218            mWindowManager.dismissKeyguard();
219
220            // If we are about to unocclude the Keyguard, but we can dismiss it without security,
221            // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
222            if (mKeyguardShowing && canDismissKeyguard()
223                    && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
224                mWindowManager.prepareAppTransition(mBeforeUnoccludeTransit,
225                        false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
226                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
227                mWindowManager.executeAppTransition();
228            }
229        }
230    }
231
232    /**
233     * @return true if Keyguard can be currently dismissed without entering credentials.
234     */
235    private boolean canDismissKeyguard() {
236        return mWindowManager.isKeyguardTrusted() || !mWindowManager.isKeyguardSecure();
237    }
238
239    private int resolveOccludeTransit() {
240        if (mBeforeUnoccludeTransit != TRANSIT_UNSET
241                && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
242                && mOccluded) {
243
244            // Reuse old transit in case we are occluding Keyguard again, meaning that we never
245            // actually occclude/unocclude Keyguard, but just run a normal transition.
246            return mBeforeUnoccludeTransit;
247        } else if (!mOccluded) {
248
249            // Save transit in case we dismiss/occlude Keyguard shortly after.
250            mBeforeUnoccludeTransit = mWindowManager.getPendingAppTransition();
251            return TRANSIT_KEYGUARD_UNOCCLUDE;
252        } else {
253            return TRANSIT_KEYGUARD_OCCLUDE;
254        }
255    }
256
257    private void dismissDockedStackIfNeeded() {
258        if (mKeyguardShowing && mOccluded) {
259            // The lock screen is currently showing, but is occluded by a window that can
260            // show on top of the lock screen. In this can we want to dismiss the docked
261            // stack since it will be complicated/risky to try to put the activity on top
262            // of the lock screen in the right fullscreen configuration.
263            mStackSupervisor.moveTasksToFullscreenStackLocked(DOCKED_STACK_ID,
264                    mStackSupervisor.mFocusedStack.getStackId() == DOCKED_STACK_ID);
265        }
266    }
267
268    void dump(PrintWriter pw, String prefix) {
269        pw.println(prefix + "KeyguardController:");
270        pw.println(prefix + "  mKeyguardShowing=" + mKeyguardShowing);
271        pw.println(prefix + "  mKeyguardGoingAway=" + mKeyguardGoingAway);
272        pw.println(prefix + "  mOccluded=" + mOccluded);
273        pw.println(prefix + "  mDismissingKeyguardActivity=" + mDismissingKeyguardActivity);
274        pw.println(prefix + "  mVisibilityTransactionDepth=" + mVisibilityTransactionDepth);
275    }
276}
277