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.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
21import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
22import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
23import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
24import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
25import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
26import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
27import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
28import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
29import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
30import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
31import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_OCCLUDE;
32import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
33import static com.android.server.wm.AppTransition.TRANSIT_NONE;
34import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
35
36import android.os.IBinder;
37import android.os.RemoteException;
38import android.os.Trace;
39import android.util.Slog;
40
41import com.android.internal.policy.IKeyguardDismissCallback;
42import com.android.server.wm.WindowManagerService;
43
44import java.io.PrintWriter;
45import 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 */
53class 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
68    KeyguardController(ActivityManagerService service,
69            ActivityStackSupervisor stackSupervisor) {
70        mService = service;
71        mStackSupervisor = stackSupervisor;
72    }
73
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     */
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     */
88    boolean isKeyguardLocked() {
89        return mKeyguardShowing && !mKeyguardGoingAway;
90    }
91
92    /**
93     * Update the Keyguard showing state.
94     */
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     */
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
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
150    private void setKeyguardGoingAway(boolean keyguardGoingAway) {
151        mKeyguardGoingAway = keyguardGoingAway;
152        mWindowManager.setKeyguardGoingAway(keyguardGoingAway);
153    }
154
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
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     */
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     */
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     */
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     */
211    boolean canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked) {
212        return showWhenLocked || dismissKeyguard && !mWindowManager.isKeyguardSecure();
213    }
214
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     */
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     */
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     */
296    boolean canDismissKeyguard() {
297        return mWindowManager.isKeyguardTrusted() || !mWindowManager.isKeyguardSecure();
298    }
299
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
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
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