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 
17 package com.android.server.am;
18 
19 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
20 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
21 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
22 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
23 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
24 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
25 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
26 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
27 import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
28 import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
29 import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
30 import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
31 import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_OCCLUDE;
32 import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
33 import static com.android.server.wm.AppTransition.TRANSIT_NONE;
34 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
35 
36 import android.os.IBinder;
37 import android.os.RemoteException;
38 import android.os.Trace;
39 import android.util.Slog;
40 
41 import com.android.internal.policy.IKeyguardDismissCallback;
42 import com.android.server.wm.WindowManagerService;
43 
44 import java.io.PrintWriter;
45 import java.util.ArrayList;
46 
47 /**
48  * Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
49  * currently visible.
50  * <p>
51  * Note that everything in this class should only be accessed with the AM lock being held.
52  */
53 class KeyguardController {
54 
55     private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_AM;
56 
57     private final ActivityManagerService mService;
58     private final ActivityStackSupervisor mStackSupervisor;
59     private WindowManagerService mWindowManager;
60     private boolean mKeyguardShowing;
61     private boolean mKeyguardGoingAway;
62     private boolean mOccluded;
63     private boolean mDismissalRequested;
64     private ActivityRecord mDismissingKeyguardActivity;
65     private int mBeforeUnoccludeTransit;
66     private int mVisibilityTransactionDepth;
67 
KeyguardController(ActivityManagerService service, ActivityStackSupervisor stackSupervisor)68     KeyguardController(ActivityManagerService service,
69             ActivityStackSupervisor stackSupervisor) {
70         mService = service;
71         mStackSupervisor = stackSupervisor;
72     }
73 
setWindowManager(WindowManagerService windowManager)74     void setWindowManager(WindowManagerService windowManager) {
75         mWindowManager = windowManager;
76     }
77 
78     /**
79      * @return true if Keyguard is showing, not going away, and not being occluded, false otherwise
80      */
isKeyguardShowing()81     boolean isKeyguardShowing() {
82         return mKeyguardShowing && !mKeyguardGoingAway && !mOccluded;
83     }
84 
85     /**
86      * @return true if Keyguard is either showing or occluded, but not going away
87      */
isKeyguardLocked()88     boolean isKeyguardLocked() {
89         return mKeyguardShowing && !mKeyguardGoingAway;
90     }
91 
92     /**
93      * Update the Keyguard showing state.
94      */
setKeyguardShown(boolean showing)95     void setKeyguardShown(boolean showing) {
96         if (showing == mKeyguardShowing) {
97             return;
98         }
99         mKeyguardShowing = showing;
100         dismissDockedStackIfNeeded();
101         if (showing) {
102             setKeyguardGoingAway(false);
103             mDismissalRequested = false;
104         }
105         mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
106         mService.updateSleepIfNeededLocked();
107     }
108 
109     /**
110      * Called when Keyguard is going away.
111      *
112      * @param flags See {@link android.view.WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
113      *              etc.
114      */
keyguardGoingAway(int flags)115     void keyguardGoingAway(int flags) {
116         if (!mKeyguardShowing) {
117             return;
118         }
119         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");
120         mWindowManager.deferSurfaceLayout();
121         try {
122             setKeyguardGoingAway(true);
123             mWindowManager.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
124                     false /* alwaysKeepCurrent */, convertTransitFlags(flags),
125                     false /* forceOverride */);
126             mService.updateSleepIfNeededLocked();
127 
128             // Some stack visibility might change (e.g. docked stack)
129             mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
130             mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
131             mWindowManager.executeAppTransition();
132         } finally {
133             Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
134             mWindowManager.continueSurfaceLayout();
135             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
136 
137             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
138         }
139     }
140 
dismissKeyguard(IBinder token, IKeyguardDismissCallback callback)141     void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback) {
142         final ActivityRecord activityRecord = ActivityRecord.forTokenLocked(token);
143         if (activityRecord == null || !activityRecord.visibleIgnoringKeyguard) {
144             failCallback(callback);
145             return;
146         }
147         mWindowManager.dismissKeyguard(callback);
148     }
149 
setKeyguardGoingAway(boolean keyguardGoingAway)150     private void setKeyguardGoingAway(boolean keyguardGoingAway) {
151         mKeyguardGoingAway = keyguardGoingAway;
152         mWindowManager.setKeyguardGoingAway(keyguardGoingAway);
153     }
154 
failCallback(IKeyguardDismissCallback callback)155     private void failCallback(IKeyguardDismissCallback callback) {
156         try {
157             callback.onDismissError();
158         } catch (RemoteException e) {
159             Slog.w(TAG, "Failed to call callback", e);
160         }
161     }
162 
convertTransitFlags(int keyguardGoingAwayFlags)163     private int convertTransitFlags(int keyguardGoingAwayFlags) {
164         int result = 0;
165         if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) {
166             result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
167         }
168         if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0) {
169             result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
170         }
171         if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0) {
172             result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
173         }
174         return result;
175     }
176 
177     /**
178      * Starts a batch of visibility updates.
179      */
beginActivityVisibilityUpdate()180     void beginActivityVisibilityUpdate() {
181         mVisibilityTransactionDepth++;
182     }
183 
184     /**
185      * Ends a batch of visibility updates. After all batches are done, this method makes sure to
186      * update lockscreen occluded/dismiss state if needed.
187      */
endActivityVisibilityUpdate()188     void endActivityVisibilityUpdate() {
189         mVisibilityTransactionDepth--;
190         if (mVisibilityTransactionDepth == 0) {
191             visibilitiesUpdated();
192         }
193     }
194 
195     /**
196      * @return True if we may show an activity while Keyguard is showing because we are in the
197      *         process of dismissing it anyways, false otherwise.
198      */
canShowActivityWhileKeyguardShowing(ActivityRecord r, boolean dismissKeyguard)199     boolean canShowActivityWhileKeyguardShowing(ActivityRecord r, boolean dismissKeyguard) {
200 
201         // Allow to show it when we are about to dismiss Keyguard. This isn't allowed if r is
202         // already the dismissing activity, in which case we don't allow it to repeatedly dismiss
203         // Keyguard.
204         return dismissKeyguard && canDismissKeyguard() &&
205                 (mDismissalRequested || r != mDismissingKeyguardActivity);
206     }
207 
208     /**
209      * @return True if we may show an activity while Keyguard is occluded, false otherwise.
210      */
canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked)211     boolean canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked) {
212         return showWhenLocked || dismissKeyguard && !mWindowManager.isKeyguardSecure();
213     }
214 
visibilitiesUpdated()215     private void visibilitiesUpdated() {
216         final boolean lastOccluded = mOccluded;
217         final ActivityRecord lastDismissingKeyguardActivity = mDismissingKeyguardActivity;
218         mOccluded = false;
219         mDismissingKeyguardActivity = null;
220         final ArrayList<ActivityStack> stacks = mStackSupervisor.getStacksOnDefaultDisplay();
221         for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
222             final ActivityStack stack = stacks.get(stackNdx);
223 
224             // Only the focused stack top activity may control occluded state
225             if (mStackSupervisor.isFocusedStack(stack)) {
226 
227                 // A dismissing activity occludes Keyguard in the insecure case for legacy reasons.
228                 final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
229                 mOccluded = stack.topActivityOccludesKeyguard()
230                         || (topDismissing != null
231                                 && stack.topRunningActivityLocked() == topDismissing
232                                 && canShowWhileOccluded(true /* dismissKeyguard */,
233                                         false /* showWhenLocked */));
234             }
235             if (mDismissingKeyguardActivity == null
236                     && stack.getTopDismissingKeyguardActivity() != null) {
237                 mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
238             }
239         }
240         mOccluded |= mWindowManager.isShowingDream();
241         if (mOccluded != lastOccluded) {
242             handleOccludedChanged();
243         }
244         if (mDismissingKeyguardActivity != lastDismissingKeyguardActivity) {
245             handleDismissKeyguard();
246         }
247     }
248 
249     /**
250      * Called when occluded state changed.
251      */
handleOccludedChanged()252     private void handleOccludedChanged() {
253         mWindowManager.onKeyguardOccludedChanged(mOccluded);
254         if (isKeyguardLocked()) {
255             mWindowManager.deferSurfaceLayout();
256             try {
257                 mWindowManager.prepareAppTransition(resolveOccludeTransit(),
258                         false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
259                 mService.updateSleepIfNeededLocked();
260                 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
261                 mWindowManager.executeAppTransition();
262             } finally {
263                 mWindowManager.continueSurfaceLayout();
264             }
265         }
266         dismissDockedStackIfNeeded();
267     }
268 
269     /**
270      * Called when somebody might want to dismiss the Keyguard.
271      */
handleDismissKeyguard()272     private void handleDismissKeyguard() {
273         // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
274         // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
275         // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
276         if (!mOccluded && mDismissingKeyguardActivity != null
277                 && mWindowManager.isKeyguardSecure()) {
278             mWindowManager.dismissKeyguard(null /* callback */);
279             mDismissalRequested = true;
280 
281             // If we are about to unocclude the Keyguard, but we can dismiss it without security,
282             // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
283             if (mKeyguardShowing && canDismissKeyguard()
284                     && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
285                 mWindowManager.prepareAppTransition(mBeforeUnoccludeTransit,
286                         false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
287                 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
288                 mWindowManager.executeAppTransition();
289             }
290         }
291     }
292 
293     /**
294      * @return true if Keyguard can be currently dismissed without entering credentials.
295      */
canDismissKeyguard()296     boolean canDismissKeyguard() {
297         return mWindowManager.isKeyguardTrusted() || !mWindowManager.isKeyguardSecure();
298     }
299 
resolveOccludeTransit()300     private int resolveOccludeTransit() {
301         if (mBeforeUnoccludeTransit != TRANSIT_UNSET
302                 && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
303                 && mOccluded) {
304 
305             // Reuse old transit in case we are occluding Keyguard again, meaning that we never
306             // actually occclude/unocclude Keyguard, but just run a normal transition.
307             return mBeforeUnoccludeTransit;
308         } else if (!mOccluded) {
309 
310             // Save transit in case we dismiss/occlude Keyguard shortly after.
311             mBeforeUnoccludeTransit = mWindowManager.getPendingAppTransition();
312             return TRANSIT_KEYGUARD_UNOCCLUDE;
313         } else {
314             return TRANSIT_KEYGUARD_OCCLUDE;
315         }
316     }
317 
dismissDockedStackIfNeeded()318     private void dismissDockedStackIfNeeded() {
319         if (mKeyguardShowing && mOccluded) {
320             // The lock screen is currently showing, but is occluded by a window that can
321             // show on top of the lock screen. In this can we want to dismiss the docked
322             // stack since it will be complicated/risky to try to put the activity on top
323             // of the lock screen in the right fullscreen configuration.
324             mStackSupervisor.moveTasksToFullscreenStackLocked(DOCKED_STACK_ID,
325                     mStackSupervisor.mFocusedStack.getStackId() == DOCKED_STACK_ID);
326         }
327     }
328 
dump(PrintWriter pw, String prefix)329     void dump(PrintWriter pw, String prefix) {
330         pw.println(prefix + "KeyguardController:");
331         pw.println(prefix + "  mKeyguardShowing=" + mKeyguardShowing);
332         pw.println(prefix + "  mKeyguardGoingAway=" + mKeyguardGoingAway);
333         pw.println(prefix + "  mOccluded=" + mOccluded);
334         pw.println(prefix + "  mDismissingKeyguardActivity=" + mDismissingKeyguardActivity);
335         pw.println(prefix + "  mDismissalRequested=" + mDismissalRequested);
336         pw.println(prefix + "  mVisibilityTransactionDepth=" + mVisibilityTransactionDepth);
337     }
338 }
339