Moved Activity management out of ActivityManager to WindowManager (37/n)

What could go wrong ;)

Note that ATMS and all other activity management files are still under
the AMS lock. We will be changing them soon to use the WMS lock, but
till then it is important to take care not to use the WMS lock for
activities stuff and AMS lock for window stuff.

Test: Existing tests pass
Bug: 80414790
Change-Id: I69f5a6e40ad892557a425e1f34be987507102136
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
new file mode 100644
index 0000000..3560635
--- /dev/null
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_UNSET;
+import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
+import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
+import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
+import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
+import static com.android.server.am.KeyguardOccludedProto.DISPLAY_ID;
+import static com.android.server.am.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
+
+import java.io.PrintWriter;
+
+/**
+ * Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
+ * currently visible.
+ * <p>
+ * Note that everything in this class should only be accessed with the AM lock being held.
+ */
+class KeyguardController {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_ATM;
+
+    private final ActivityStackSupervisor mStackSupervisor;
+    private WindowManagerService mWindowManager;
+    private boolean mKeyguardShowing;
+    private boolean mAodShowing;
+    private boolean mKeyguardGoingAway;
+    private boolean mDismissalRequested;
+    private int mBeforeUnoccludeTransit;
+    private int mVisibilityTransactionDepth;
+    // TODO(b/111955725): Support multiple external displays
+    private int mSecondaryDisplayShowing = INVALID_DISPLAY;
+    private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
+    private final ActivityTaskManagerService mService;
+
+    KeyguardController(ActivityTaskManagerService service,
+            ActivityStackSupervisor stackSupervisor) {
+        mService = service;
+        mStackSupervisor = stackSupervisor;
+    }
+
+    void setWindowManager(WindowManagerService windowManager) {
+        mWindowManager = windowManager;
+    }
+
+    /**
+     * @return true if either Keyguard or AOD are showing, not going away, and not being occluded
+     *         on the given display, false otherwise
+     */
+    boolean isKeyguardOrAodShowing(int displayId) {
+        return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway
+                && !isDisplayOccluded(displayId);
+    }
+
+    /**
+     * @return true if Keyguard is showing, not going away, and not being occluded on the given
+     *         display, false otherwise
+     */
+    boolean isKeyguardShowing(int displayId) {
+        return mKeyguardShowing && !mKeyguardGoingAway && !isDisplayOccluded(displayId);
+    }
+
+    /**
+     * @return true if Keyguard is either showing or occluded, but not going away
+     */
+    boolean isKeyguardLocked() {
+        return mKeyguardShowing && !mKeyguardGoingAway;
+    }
+
+    /**
+     * @return {@code true} if the keyguard is going away, {@code false} otherwise.
+     */
+    boolean isKeyguardGoingAway() {
+        // Also check keyguard showing in case value is stale.
+        return mKeyguardGoingAway && mKeyguardShowing;
+    }
+
+    /**
+     * Update the Keyguard showing state.
+     */
+    void setKeyguardShown(boolean keyguardShowing, boolean aodShowing,
+            int secondaryDisplayShowing) {
+        boolean showingChanged = keyguardShowing != mKeyguardShowing || aodShowing != mAodShowing;
+        // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
+        showingChanged |= mKeyguardGoingAway && keyguardShowing;
+        if (!showingChanged && secondaryDisplayShowing == mSecondaryDisplayShowing) {
+            return;
+        }
+        mKeyguardShowing = keyguardShowing;
+        mAodShowing = aodShowing;
+        mSecondaryDisplayShowing = secondaryDisplayShowing;
+        mWindowManager.setAodShowing(aodShowing);
+        if (showingChanged) {
+            dismissDockedStackIfNeeded();
+            setKeyguardGoingAway(false);
+            // TODO(b/113840485): Check usage for non-default display
+            mWindowManager.setKeyguardOrAodShowingOnDefaultDisplay(
+                    isKeyguardOrAodShowing(DEFAULT_DISPLAY));
+            if (keyguardShowing) {
+                mDismissalRequested = false;
+            }
+        }
+        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        updateKeyguardSleepToken();
+    }
+
+    /**
+     * Called when Keyguard is going away.
+     *
+     * @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
+     *              etc.
+     */
+    void keyguardGoingAway(int flags) {
+        if (!mKeyguardShowing) {
+            return;
+        }
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");
+        mWindowManager.deferSurfaceLayout();
+        try {
+            setKeyguardGoingAway(true);
+            mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+                    .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
+                            false /* alwaysKeepCurrent */, convertTransitFlags(flags),
+                            false /* forceOverride */);
+            updateKeyguardSleepToken();
+
+            // Some stack visibility might change (e.g. docked stack)
+            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+            mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
+            mWindowManager.executeAppTransition();
+        } finally {
+            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
+            mWindowManager.continueSurfaceLayout();
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message) {
+        final ActivityRecord activityRecord = ActivityRecord.forTokenLocked(token);
+        if (activityRecord == null || !activityRecord.visibleIgnoringKeyguard) {
+            failCallback(callback);
+            return;
+        }
+        Slog.i(TAG, "Activity requesting to dismiss Keyguard: " + activityRecord);
+
+        // If the client has requested to dismiss the keyguard and the Activity has the flag to
+        // turn the screen on, wakeup the screen if it's the top Activity.
+        if (activityRecord.getTurnScreenOnFlag() && activityRecord.isTopRunningActivity()) {
+            mStackSupervisor.wakeUp("dismissKeyguard");
+        }
+
+        mWindowManager.dismissKeyguard(callback, message);
+    }
+
+    private void setKeyguardGoingAway(boolean keyguardGoingAway) {
+        mKeyguardGoingAway = keyguardGoingAway;
+        mWindowManager.setKeyguardGoingAway(keyguardGoingAway);
+    }
+
+    private void failCallback(IKeyguardDismissCallback callback) {
+        try {
+            callback.onDismissError();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to call callback", e);
+        }
+    }
+
+    private int convertTransitFlags(int keyguardGoingAwayFlags) {
+        int result = 0;
+        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) {
+            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+        }
+        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0) {
+            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+        }
+        if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0) {
+            result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+        }
+        return result;
+    }
+
+    /**
+     * Starts a batch of visibility updates.
+     */
+    void beginActivityVisibilityUpdate() {
+        mVisibilityTransactionDepth++;
+    }
+
+    /**
+     * Ends a batch of visibility updates. After all batches are done, this method makes sure to
+     * update lockscreen occluded/dismiss state if needed.
+     */
+    void endActivityVisibilityUpdate() {
+        mVisibilityTransactionDepth--;
+        if (mVisibilityTransactionDepth == 0) {
+            visibilitiesUpdated();
+        }
+    }
+
+    /**
+     * @return True if we may show an activity while Keyguard is showing because we are in the
+     *         process of dismissing it anyways, false otherwise.
+     */
+    boolean canShowActivityWhileKeyguardShowing(ActivityRecord r, boolean dismissKeyguard) {
+
+        // Allow to show it when we are about to dismiss Keyguard. This isn't allowed if r is
+        // already the dismissing activity, in which case we don't allow it to repeatedly dismiss
+        // Keyguard.
+        return dismissKeyguard && canDismissKeyguard() && !mAodShowing
+                && (mDismissalRequested
+                || getDisplay(r.getDisplayId()).mDismissingKeyguardActivity != r);
+    }
+
+    /**
+     * @return True if we may show an activity while Keyguard is occluded, false otherwise.
+     */
+    boolean canShowWhileOccluded(boolean dismissKeyguard, boolean showWhenLocked) {
+        return showWhenLocked || dismissKeyguard && !mWindowManager.isKeyguardSecure();
+    }
+
+    private void visibilitiesUpdated() {
+        boolean requestDismissKeyguard = false;
+        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
+            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+            final KeyguardDisplayState state = getDisplay(display.mDisplayId);
+            state.visibilitiesUpdated(this, display);
+            requestDismissKeyguard |= state.mRequestDismissKeyguard;
+        }
+
+        // Dismissing Keyguard happens globally using the information from all displays.
+        if (requestDismissKeyguard) {
+            handleDismissKeyguard();
+        }
+    }
+
+    /**
+     * Called when occluded state changed.
+     */
+    private void handleOccludedChanged() {
+        mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY));
+        if (isKeyguardLocked()) {
+            mWindowManager.deferSurfaceLayout();
+            try {
+                mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+                        .prepareAppTransition(resolveOccludeTransit(),
+                                false /* alwaysKeepCurrent */, 0 /* flags */,
+                                true /* forceOverride */);
+                updateKeyguardSleepToken();
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mWindowManager.executeAppTransition();
+            } finally {
+                mWindowManager.continueSurfaceLayout();
+            }
+        }
+        dismissDockedStackIfNeeded();
+    }
+
+    /**
+     * Called when somebody wants to dismiss the Keyguard via the flag.
+     */
+    private void handleDismissKeyguard() {
+        // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
+        // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
+        // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
+        if (mWindowManager.isKeyguardSecure()) {
+            mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
+            mDismissalRequested = true;
+
+            // If we are about to unocclude the Keyguard, but we can dismiss it without security,
+            // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
+            final DisplayWindowController dwc =
+                    mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+            if (mKeyguardShowing && canDismissKeyguard()
+                    && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
+                dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
+                        0 /* flags */, true /* forceOverride */);
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mWindowManager.executeAppTransition();
+            }
+        }
+    }
+
+    private boolean isDisplayOccluded(int displayId) {
+        return getDisplay(displayId).mOccluded;
+    }
+
+    /**
+     * @return true if Keyguard can be currently dismissed without entering credentials.
+     */
+    boolean canDismissKeyguard() {
+        return mWindowManager.isKeyguardTrusted() || !mWindowManager.isKeyguardSecure();
+    }
+
+    private int resolveOccludeTransit() {
+        final DisplayWindowController dwc =
+                mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+        if (mBeforeUnoccludeTransit != TRANSIT_UNSET
+                && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
+                // TODO(b/113840485): Handle app transition for individual display.
+                && isDisplayOccluded(DEFAULT_DISPLAY)) {
+
+            // Reuse old transit in case we are occluding Keyguard again, meaning that we never
+            // actually occclude/unocclude Keyguard, but just run a normal transition.
+            return mBeforeUnoccludeTransit;
+            // TODO(b/113840485): Handle app transition for individual display.
+        } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) {
+
+            // Save transit in case we dismiss/occlude Keyguard shortly after.
+            mBeforeUnoccludeTransit = dwc.getPendingAppTransition();
+            return TRANSIT_KEYGUARD_UNOCCLUDE;
+        } else {
+            return TRANSIT_KEYGUARD_OCCLUDE;
+        }
+    }
+
+    private void dismissDockedStackIfNeeded() {
+        // TODO(b/113840485): Handle docked stack for individual display.
+        if (mKeyguardShowing && isDisplayOccluded(DEFAULT_DISPLAY)) {
+            // The lock screen is currently showing, but is occluded by a window that can
+            // show on top of the lock screen. In this can we want to dismiss the docked
+            // stack since it will be complicated/risky to try to put the activity on top
+            // of the lock screen in the right fullscreen configuration.
+            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+            if (stack == null) {
+                return;
+            }
+            mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
+                    stack.isFocusedStackOnDisplay());
+        }
+    }
+
+    private void updateKeyguardSleepToken() {
+        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
+            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+            final KeyguardDisplayState state = getDisplay(display.mDisplayId);
+            if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) {
+                state.acquiredSleepToken();
+            } else if (!isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken != null) {
+                state.releaseSleepToken();
+            }
+        }
+    }
+
+    private KeyguardDisplayState getDisplay(int displayId) {
+        if (mDisplayStates.get(displayId) == null) {
+            mDisplayStates.append(displayId,
+                    new KeyguardDisplayState(mService, displayId));
+        }
+        return mDisplayStates.get(displayId);
+    }
+
+    void onDisplayRemoved(int displayId) {
+        if (mDisplayStates.get(displayId) != null) {
+            mDisplayStates.get(displayId).onRemoved();
+            mDisplayStates.remove(displayId);
+        }
+    }
+
+    /** Represents Keyguard state per individual display. */
+    private static class KeyguardDisplayState {
+        private final int mDisplayId;
+        private boolean mOccluded;
+        private ActivityRecord mDismissingKeyguardActivity;
+        private boolean mRequestDismissKeyguard;
+        private final ActivityTaskManagerService mService;
+        private SleepToken mSleepToken;
+
+        KeyguardDisplayState(ActivityTaskManagerService service, int displayId) {
+            mService = service;
+            mDisplayId = displayId;
+        }
+
+        void onRemoved() {
+            mDismissingKeyguardActivity = null;
+            releaseSleepToken();
+        }
+
+        void acquiredSleepToken() {
+            if (mSleepToken == null) {
+                mSleepToken = mService.acquireSleepToken("keyguard", mDisplayId);
+            }
+        }
+
+        void releaseSleepToken() {
+            if (mSleepToken != null) {
+                mSleepToken.release();
+                mSleepToken = null;
+            }
+        }
+
+        void visibilitiesUpdated(KeyguardController controller, ActivityDisplay display) {
+            final boolean lastOccluded = mOccluded;
+            final ActivityRecord lastDismissActivity = mDismissingKeyguardActivity;
+            mRequestDismissKeyguard = false;
+            mOccluded = false;
+            mDismissingKeyguardActivity = null;
+
+            // Only the top activity of the focused stack on each display may control it's
+            // occluded state.
+            final ActivityStack focusedStack = display.getFocusedStack();
+            if (focusedStack != null) {
+                final ActivityRecord topDismissing =
+                        focusedStack.getTopDismissingKeyguardActivity();
+                mOccluded = focusedStack.topActivityOccludesKeyguard() || (topDismissing != null
+                                && focusedStack.topRunningActivityLocked() == topDismissing
+                                && controller.canShowWhileOccluded(
+                                true /* dismissKeyguard */,
+                                false /* showWhenLocked */));
+                if (focusedStack.getTopDismissingKeyguardActivity() != null) {
+                    mDismissingKeyguardActivity = focusedStack.getTopDismissingKeyguardActivity();
+                }
+                mOccluded |= controller.mWindowManager.isShowingDream();
+            }
+
+            // TODO(b/113840485): Handle app transition for individual display.
+            // For now, only default display can change occluded.
+            if (lastOccluded != mOccluded && mDisplayId == DEFAULT_DISPLAY) {
+                controller.handleOccludedChanged();
+            }
+            if (lastDismissActivity != mDismissingKeyguardActivity && !mOccluded
+                    && mDismissingKeyguardActivity != null
+                    && controller.mWindowManager.isKeyguardSecure()) {
+                mRequestDismissKeyguard = true;
+            }
+        }
+
+        void dumpStatus(PrintWriter pw, String prefix) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append(prefix);
+            sb.append("  Occluded=").append(mOccluded)
+                    .append(" DismissingKeyguardActivity=")
+                    .append(mDismissingKeyguardActivity)
+                    .append(" at display=")
+                    .append(mDisplayId);
+            pw.println(sb.toString());
+        }
+
+        void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+            proto.write(DISPLAY_ID, mDisplayId);
+            proto.write(KEYGUARD_OCCLUDED, mOccluded);
+            proto.end(token);
+        }
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        pw.println(prefix + "KeyguardController:");
+        pw.println(prefix + "  mKeyguardShowing=" + mKeyguardShowing);
+        pw.println(prefix + "  mAodShowing=" + mAodShowing);
+        pw.println(prefix + "  mKeyguardGoingAway=" + mKeyguardGoingAway);
+        dumpDisplayStates(pw, prefix);
+        pw.println(prefix + "  mDismissalRequested=" + mDismissalRequested);
+        pw.println(prefix + "  mVisibilityTransactionDepth=" + mVisibilityTransactionDepth);
+    }
+
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(KEYGUARD_SHOWING, mKeyguardShowing);
+        writeDisplayStatesToProto(proto, KEYGUARD_OCCLUDED_STATES);
+        proto.end(token);
+    }
+
+    private void dumpDisplayStates(PrintWriter pw, String prefix) {
+        for (int i = 0; i < mDisplayStates.size(); i++) {
+            mDisplayStates.valueAt(i).dumpStatus(pw, prefix);
+        }
+    }
+
+    private void writeDisplayStatesToProto(ProtoOutputStream proto, long fieldId) {
+        for (int i = 0; i < mDisplayStates.size(); i++) {
+            mDisplayStates.valueAt(i).writeToProto(proto, fieldId);
+        }
+    }
+}