Wale Ogunwale | 076c3b1 | 2019-11-20 12:17:22 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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.wm; |
| 18 | |
| 19 | import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; |
| 20 | |
| 21 | import static com.android.server.wm.ActivityStack.TAG_VISIBILITY; |
| 22 | import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY; |
| 23 | |
| 24 | import android.util.Slog; |
| 25 | |
| 26 | import com.android.internal.util.function.pooled.PooledConsumer; |
| 27 | import com.android.internal.util.function.pooled.PooledLambda; |
| 28 | |
| 29 | /** Helper class to ensure activities are in the right visible state for a container. */ |
| 30 | class EnsureActivitiesVisibleHelper { |
| 31 | private final ActivityStack mContiner; |
| 32 | private ActivityRecord mTop; |
| 33 | private ActivityRecord mStarting; |
| 34 | private boolean mAboveTop; |
| 35 | private boolean mContainerShouldBeVisible; |
| 36 | private boolean mBehindFullscreenActivity; |
| 37 | private int mConfigChanges; |
| 38 | private boolean mPreserveWindows; |
| 39 | private boolean mNotifyClients; |
| 40 | |
| 41 | EnsureActivitiesVisibleHelper(ActivityStack container) { |
| 42 | mContiner = container; |
| 43 | } |
| 44 | |
| 45 | void reset(ActivityRecord starting, int configChanges, boolean preserveWindows, |
| 46 | boolean notifyClients) { |
| 47 | mStarting = starting; |
| 48 | mTop = mContiner.topRunningActivityLocked(); |
| 49 | // If the top activity is not fullscreen, then we need to make sure any activities under it |
| 50 | // are now visible. |
| 51 | mAboveTop = mTop != null; |
| 52 | mContainerShouldBeVisible = mContiner.shouldBeVisible(mStarting); |
| 53 | mBehindFullscreenActivity = !mContainerShouldBeVisible; |
| 54 | mConfigChanges = configChanges; |
| 55 | mPreserveWindows = preserveWindows; |
| 56 | mNotifyClients = notifyClients; |
| 57 | } |
| 58 | |
| 59 | /** |
| 60 | * Ensure visibility with an option to also update the configuration of visible activities. |
| 61 | * @see ActivityStack#ensureActivitiesVisible(ActivityRecord, int, boolean) |
| 62 | * @see RootActivityContainer#ensureActivitiesVisible(ActivityRecord, int, boolean) |
| 63 | */ |
| 64 | void process(ActivityRecord starting, int configChanges, boolean preserveWindows, |
| 65 | boolean notifyClients) { |
| 66 | reset(starting, configChanges, preserveWindows, notifyClients); |
| 67 | |
| 68 | if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop |
| 69 | + " configChanges=0x" + Integer.toHexString(configChanges)); |
| 70 | if (mTop != null) { |
| 71 | mContiner.checkTranslucentActivityWaiting(mTop); |
| 72 | } |
| 73 | |
| 74 | // We should not resume activities that being launched behind because these |
| 75 | // activities are actually behind other fullscreen activities, but still required |
| 76 | // to be visible (such as performing Recents animation). |
| 77 | final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind |
| 78 | && mContiner.isFocusable() && mContiner.isInStackLocked(starting) == null; |
| 79 | |
| 80 | final PooledConsumer f = PooledLambda.obtainConsumer( |
| 81 | EnsureActivitiesVisibleHelper::setActivityVisibilityState, this, |
| 82 | PooledLambda.__(ActivityRecord.class), resumeTopActivity); |
| 83 | mContiner.forAllActivities(f); |
| 84 | f.recycle(); |
| 85 | } |
| 86 | |
| 87 | private void setActivityVisibilityState(ActivityRecord r, final boolean resumeTopActivity) { |
| 88 | final boolean isTop = r == mTop; |
| 89 | if (mAboveTop && !isTop) { |
| 90 | return; |
| 91 | } |
| 92 | mAboveTop = false; |
| 93 | |
| 94 | final boolean reallyVisible = r.shouldBeVisible( |
| 95 | mBehindFullscreenActivity, false /* ignoringKeyguard */); |
| 96 | |
| 97 | // Check whether activity should be visible without Keyguard influence |
| 98 | if (r.visibleIgnoringKeyguard) { |
| 99 | if (r.occludesParent()) { |
| 100 | // At this point, nothing else needs to be shown in this task. |
| 101 | if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r |
| 102 | + " stackVisible=" + mContainerShouldBeVisible |
| 103 | + " behindFullscreen=" + mBehindFullscreenActivity); |
| 104 | mBehindFullscreenActivity = true; |
| 105 | } else { |
| 106 | mBehindFullscreenActivity = false; |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | if (reallyVisible) { |
| 111 | if (r.finishing) { |
| 112 | return; |
| 113 | } |
| 114 | if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r |
| 115 | + " finishing=" + r.finishing + " state=" + r.getState()); |
| 116 | // First: if this is not the current activity being started, make |
| 117 | // sure it matches the current configuration. |
| 118 | if (r != mStarting && mNotifyClients) { |
| 119 | r.ensureActivityConfiguration(0 /* globalChanges */, mPreserveWindows, |
| 120 | true /* ignoreVisibility */); |
| 121 | } |
| 122 | |
| 123 | if (!r.attachedToProcess()) { |
| 124 | makeVisibleAndRestartIfNeeded(mStarting, mConfigChanges, isTop, |
| 125 | resumeTopActivity && isTop, r); |
| 126 | } else if (r.mVisibleRequested) { |
| 127 | // If this activity is already visible, then there is nothing to do here. |
| 128 | if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, |
| 129 | "Skipping: already visible at " + r); |
| 130 | |
| 131 | if (r.mClientVisibilityDeferred && mNotifyClients) { |
| 132 | r.makeClientVisible(); |
| 133 | } |
| 134 | |
| 135 | r.handleAlreadyVisible(); |
| 136 | if (mNotifyClients) { |
| 137 | r.makeActiveIfNeeded(mStarting); |
| 138 | } |
| 139 | } else { |
| 140 | r.makeVisibleIfNeeded(mStarting, mNotifyClients); |
| 141 | } |
| 142 | // Aggregate current change flags. |
| 143 | mConfigChanges |= r.configChangeFlags; |
| 144 | } else { |
| 145 | if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r |
| 146 | + " finishing=" + r.finishing + " state=" + r.getState() |
| 147 | + " stackShouldBeVisible=" + mContainerShouldBeVisible |
| 148 | + " behindFullscreenActivity=" + mBehindFullscreenActivity |
| 149 | + " mLaunchTaskBehind=" + r.mLaunchTaskBehind); |
| 150 | r.makeInvisible(); |
| 151 | } |
| 152 | |
| 153 | final int windowingMode = mContiner.getWindowingMode(); |
| 154 | if (windowingMode == WINDOWING_MODE_FREEFORM) { |
| 155 | // The visibility of tasks and the activities they contain in freeform stack are |
| 156 | // determined individually unlike other stacks where the visibility or fullscreen |
| 157 | // status of an activity in a previous task affects other. |
| 158 | mBehindFullscreenActivity = !mContainerShouldBeVisible; |
| 159 | } else if (mContiner.isActivityTypeHome()) { |
| 160 | if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner |
| 161 | + " stackShouldBeVisible=" + mContainerShouldBeVisible |
| 162 | + " behindFullscreenActivity=" + mBehindFullscreenActivity); |
| 163 | // No other task in the home stack should be visible behind the home activity. |
| 164 | // Home activities is usually a translucent activity with the wallpaper behind |
| 165 | // them. However, when they don't have the wallpaper behind them, we want to |
| 166 | // show activities in the next application stack behind them vs. another |
| 167 | // task in the home stack like recents. |
| 168 | mBehindFullscreenActivity = true; |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | private void makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges, |
| 173 | boolean isTop, boolean andResume, ActivityRecord r) { |
| 174 | // We need to make sure the app is running if it's the top, or it is just made visible from |
| 175 | // invisible. If the app is already visible, it must have died while it was visible. In this |
| 176 | // case, we'll show the dead window but will not restart the app. Otherwise we could end up |
| 177 | // thrashing. |
| 178 | if (!isTop && r.mVisibleRequested) { |
| 179 | return; |
| 180 | } |
| 181 | |
| 182 | // This activity needs to be visible, but isn't even running... |
| 183 | // get it started and resume if no other stack in this stack is resumed. |
| 184 | if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r); |
| 185 | if (r != starting) { |
| 186 | r.startFreezingScreenLocked(configChanges); |
| 187 | } |
| 188 | if (!r.mVisibleRequested || r.mLaunchTaskBehind) { |
| 189 | if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r); |
| 190 | r.setVisibility(true); |
| 191 | } |
| 192 | if (r != starting) { |
| 193 | mContiner.mStackSupervisor.startSpecificActivity(r, andResume, true /* checkConfig */); |
| 194 | } |
| 195 | } |
| 196 | } |