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