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