Merge "Associate starting window in task fragment with base task" into sc-v2-dev am: 25f50a91e8

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15371455

Change-Id: I813776a9f0e4b0ebf6fcb6a0cf5c515cf0e8cf37
This commit is contained in:
Riddle Hsu
2021-08-03 00:29:07 +00:00
committed by Automerger Merge Worker
8 changed files with 203 additions and 59 deletions

View File

@@ -330,8 +330,6 @@ import com.android.internal.policy.AttributeCache;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.PendingIntentRecord;
@@ -746,6 +744,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean startingDisplayed;
boolean startingMoved;
/**
* If it is non-null, it requires all activities who have the same starting data to be drawn
* to remove the starting window.
* TODO(b/189385912): Remove starting window related fields after migrating them to task.
*/
private StartingData mSharedStartingData;
boolean mHandleExitSplashScreen;
@TransferSplashScreenState
int mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
@@ -1085,6 +1090,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
pw.print(" mIsExiting="); pw.println(mIsExiting);
}
if (mSharedStartingData != null) {
pw.println(prefix + "mSharedStartingData=" + mSharedStartingData);
}
if (mStartingWindow != null || mStartingSurface != null
|| startingDisplayed || startingMoved || mVisibleSetFromTransferredStartingWindow) {
pw.print(prefix); pw.print("startingWindow="); pw.print(mStartingWindow);
@@ -1379,9 +1387,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
@Override
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
final Task oldTask = oldParent != null ? ((TaskFragment) oldParent).getTask() : null;
final Task newTask = newParent != null ? ((TaskFragment) newParent).getTask() : null;
void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
final TaskFragment oldParent = (TaskFragment) rawOldParent;
final TaskFragment newParent = (TaskFragment) rawNewParent;
final Task oldTask = oldParent != null ? oldParent.getTask() : null;
final Task newTask = newParent != null ? newParent.getTask() : null;
this.task = newTask;
super.onParentChanged(newParent, oldParent);
@@ -1440,11 +1450,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
updateColorTransform();
if (oldParent != null) {
((TaskFragment) oldParent).cleanUpActivityReferences(this);
oldParent.cleanUpActivityReferences(this);
}
if (newParent != null && isState(RESUMED)) {
((TaskFragment) newParent).setResumedActivity(this, "onParentChanged");
newParent.setResumedActivity(this, "onParentChanged");
if (mStartingWindow != null && mStartingData != null
&& mStartingData.mAssociatedTask == null && newParent.isEmbedded()) {
// The starting window should keep covering its task when the activity is
// reparented to a task fragment that may not fill the task bounds.
associateStartingDataWithTask();
overrideConfigurationPropagation(mStartingWindow, task);
}
}
if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -2005,7 +2022,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@VisibleForTesting
boolean addStartingWindow(String pkg, int resolvedTheme, CompatibilityInfo compatInfo,
CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
ActivityRecord from, boolean newTask, boolean taskSwitch, boolean processRunning,
boolean allowTaskSnapshot, boolean activityCreated, boolean useEmpty) {
// If the display is frozen, we won't do anything until the actual window is
// displayed so there is no reason to put in the starting window.
@@ -2064,7 +2081,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
applyStartingWindowTheme(pkg, resolvedTheme);
if (transferStartingWindow(transferFrom)) {
if (from != null && transferStartingWindow(from)) {
return true;
}
@@ -2089,6 +2106,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SnapshotStartingData");
mStartingData = new SnapshotStartingData(mWmService, snapshot, typeParams);
if (task.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
// Associate with the task so if this activity is resized by task fragment later, the
// starting window can keep the same bounds as the task.
associateStartingDataWithTask();
}
scheduleAddStartingWindow();
return true;
}
@@ -2337,6 +2359,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
void attachStartingWindow(@NonNull WindowState startingWindow) {
mStartingWindow = startingWindow;
if (mStartingData != null && mStartingData.mAssociatedTask != null) {
// Associate the configuration of starting window with the task.
overrideConfigurationPropagation(startingWindow, mStartingData.mAssociatedTask);
}
}
private void associateStartingDataWithTask() {
mStartingData.mAssociatedTask = task;
task.forAllActivities(r -> {
if (r.mVisibleRequested && !r.firstWindowDrawn) {
r.mSharedStartingData = mStartingData;
}
});
}
void removeStartingWindow() {
if (transferSplashScreenIfNeeded()) {
return;
@@ -2346,6 +2385,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void removeStartingWindowAnimation(boolean prepareAnimation) {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
if (mSharedStartingData != null) {
mSharedStartingData.mAssociatedTask.forAllActivities(r -> {
r.mSharedStartingData = null;
});
}
if (mStartingWindow == null) {
if (mStartingData != null) {
// Starting window has not been added yet, but it is scheduled to be added.
@@ -3864,12 +3908,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
boolean transferStartingWindow(IBinder transferFrom) {
final ActivityRecord fromActivity = getDisplayContent().getActivityRecord(transferFrom);
if (fromActivity == null) {
return false;
}
private boolean transferStartingWindow(@NonNull ActivityRecord fromActivity) {
final WindowState tStartingWindow = fromActivity.mStartingWindow;
if (tStartingWindow != null && fromActivity.mStartingSurface != null) {
// In this case, the starting icon has already been displayed, so start
@@ -3890,6 +3929,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Transfer the starting window over to the new token.
mStartingData = fromActivity.mStartingData;
mSharedStartingData = fromActivity.mSharedStartingData;
mStartingSurface = fromActivity.mStartingSurface;
startingDisplayed = fromActivity.startingDisplayed;
fromActivity.startingDisplayed = false;
@@ -3952,6 +3992,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
"Moving pending starting from %s to %s", fromActivity, this);
mStartingData = fromActivity.mStartingData;
mSharedStartingData = fromActivity.mSharedStartingData;
fromActivity.mStartingData = null;
fromActivity.startingMoved = true;
scheduleAddStartingWindow();
@@ -3970,16 +4011,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* immediately finishes after, so we have to transfer T to M.
*/
void transferStartingWindowFromHiddenAboveTokenIfNeeded() {
final PooledFunction p = PooledLambda.obtainFunction(ActivityRecord::transferStartingWindow,
this, PooledLambda.__(ActivityRecord.class));
task.forAllActivities(p);
p.recycle();
}
private boolean transferStartingWindow(ActivityRecord fromActivity) {
if (fromActivity == this) return true;
return !fromActivity.mVisibleRequested && transferStartingWindow(fromActivity.token);
task.forAllActivities(fromActivity -> {
if (fromActivity == this) return true;
return !fromActivity.mVisibleRequested && transferStartingWindow(fromActivity);
});
}
void checkKeyguardFlagsChanged() {
@@ -5160,6 +5195,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void notifyAppStopped() {
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppStopped: %s", this);
mAppStopped = true;
firstWindowDrawn = false;
// This is to fix the edge case that auto-enter-pip is finished in Launcher but app calls
// setAutoEnterEnabled(false) and transitions to STOPPED state, see b/191930787.
// Clear any surface transactions and content overlay in this case.
@@ -5923,7 +5959,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
void onFirstWindowDrawn(WindowState win) {
firstWindowDrawn = true;
// stop tracking
mSplashScreenStyleEmpty = true;
@@ -5939,7 +5975,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// own stuff.
win.cancelAnimation();
}
removeStartingWindow();
// Remove starting window directly if is in a pure task. Otherwise if it is associated with
// a task (e.g. nested task fragment), then remove only if all visible windows in the task
// are drawn.
final Task associatedTask =
mSharedStartingData != null ? mSharedStartingData.mAssociatedTask : null;
if (associatedTask == null) {
removeStartingWindow();
} else if (associatedTask.getActivity(
r -> r.mVisibleRequested && !r.firstWindowDrawn) == null) {
// The last drawn activity may not be the one that owns the starting window.
final ActivityRecord r = associatedTask.topActivityContainsStartingWindow();
if (r != null) {
r.removeStartingWindow();
}
}
updateReportedVisibilityLocked();
}
@@ -6510,8 +6561,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
prev != null ? prev.appToken : null,
newTask || newSingleActivity, taskSwitch, isProcessRunning(),
prev, newTask || newSingleActivity, taskSwitch, isProcessRunning(),
allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty);
if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
Slog.d(TAG, "Scheduled starting window for " + this);

View File

@@ -32,6 +32,12 @@ public abstract class StartingData {
*/
boolean mIsTransitionForward;
/**
* Non-null if the starting window should cover the bounds of associated task. It is assigned
* when the parent activity of starting window may be put in a partial area of the task.
*/
Task mAssociatedTask;
protected StartingData(WindowManagerService service, int typeParams) {
mService = service;
mTypeParams = typeParams;

View File

@@ -628,7 +628,7 @@ class Task extends TaskFragment {
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear,
boolean _removeWithTaskOrganizer) {
super(atmService, null /* fragmentToken */, _createdByOrganizer);
super(atmService, null /* fragmentToken */, _createdByOrganizer, false /* isEmbedded */);
mTaskId = _taskId;
mUserId = _userId;
@@ -707,13 +707,13 @@ class Task extends TaskFragment {
return this;
}
private void cleanUpResourcesForDestroy(ConfigurationContainer oldParent) {
private void cleanUpResourcesForDestroy(WindowContainer<?> oldParent) {
if (hasChild()) {
return;
}
// This task is going away, so save the last state if necessary.
saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
saveLaunchingStateIfNeeded(oldParent.getDisplayContent());
// TODO: VI what about activity?
final boolean isVoiceSession = voiceSession != null;
@@ -1144,11 +1144,11 @@ class Task extends TaskFragment {
}
@Override
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
final DisplayContent display = newParent != null
? ((WindowContainer) newParent).getDisplayContent() : null;
final DisplayContent oldDisplay = oldParent != null
? ((WindowContainer) oldParent).getDisplayContent() : null;
void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
final WindowContainer<?> newParent = (WindowContainer<?>) rawNewParent;
final WindowContainer<?> oldParent = (WindowContainer<?>) rawOldParent;
final DisplayContent display = newParent != null ? newParent.getDisplayContent() : null;
final DisplayContent oldDisplay = oldParent != null ? oldParent.getDisplayContent() : null;
mPrevDisplayId = (oldDisplay != null) ? oldDisplay.mDisplayId : INVALID_DISPLAY;
@@ -1189,7 +1189,7 @@ class Task extends TaskFragment {
}
if (oldParent != null) {
final Task oldParentTask = ((WindowContainer) oldParent).asTask();
final Task oldParentTask = oldParent.asTask();
if (oldParentTask != null) {
final PooledConsumer c = PooledLambda.obtainConsumer(
Task::cleanUpActivityReferences, oldParentTask,
@@ -5164,21 +5164,17 @@ class Task extends TaskFragment {
// "has the same starting icon" as the next one. This allows the
// window manager to keep the previous window it had previously
// created, if it still had one.
Task prevTask = r.getTask();
ActivityRecord prev = prevTask.getActivity(
a -> a.mStartingData != null && a.okToShowLocked());
if (prev != null) {
// We don't want to reuse the previous starting preview if:
// (1) The current activity is in a different task.
if (prev.getTask() != prevTask) {
prev = null;
}
// (2) The current activity is already displayed.
else if (prev.nowVisible) {
prev = null;
}
Task baseTask = r.getTask();
if (baseTask.isEmbedded()) {
// If the task is embedded in a task fragment, there may have an existing
// starting window in the parent task. This allows the embedded activities
// to share the starting window and make sure that the window can have top
// z-order by transferring to the top activity.
baseTask = baseTask.getParent().asTaskFragment().getTask();
}
final ActivityRecord prev = baseTask.getActivity(
a -> a.mStartingData != null && a.okToShowLocked());
r.showStartingWindow(prev, newTask, isTaskSwitch,
true /* startActivity */, sourceRecord);
}

View File

@@ -207,6 +207,9 @@ class TaskFragment extends WindowContainer<WindowContainer> {
@VisibleForTesting
boolean mCreatedByOrganizer;
/** Whether this TaskFragment is embedded in a task. */
private final boolean mIsEmbedded;
/** Organizer that organizing this TaskFragment. */
@Nullable
private ITaskFragmentOrganizer mTaskFragmentOrganizer;
@@ -269,14 +272,21 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
}
/** Creates an embedded task fragment. */
TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
boolean createdByOrganizer) {
this(atmService, fragmentToken, createdByOrganizer, true /* isEmbedded */);
}
TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
boolean createdByOrganizer, boolean isEmbedded) {
super(atmService.mWindowManager);
mAtmService = atmService;
mTaskSupervisor = mAtmService.mTaskSupervisor;
mRootWindowContainer = mAtmService.mRootWindowContainer;
mCreatedByOrganizer = createdByOrganizer;
mIsEmbedded = isEmbedded;
mTaskFragmentOrganizerController =
mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
mFragmentToken = fragmentToken;
@@ -389,6 +399,18 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return this;
}
/** Returns {@code true} if this is a container for embedded activities or tasks. */
boolean isEmbedded() {
if (mIsEmbedded) {
return true;
}
final WindowContainer<?> parent = getParent();
if (parent != null) {
final TaskFragment taskFragment = parent.asTaskFragment();
return taskFragment != null && taskFragment.isEmbedded();
}
return false;
}
/**
* Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}.

View File

@@ -3399,6 +3399,29 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
unregisterConfigurationChangeListener(listener);
}
/**
* Forces the receiver container to always use the configuration of the supplier container as
* its requested override configuration. It allows to propagate configuration without changing
* the relationship between child and parent.
*/
static void overrideConfigurationPropagation(WindowContainer<?> receiver,
WindowContainer<?> supplier) {
final ConfigurationContainerListener listener = new ConfigurationContainerListener() {
@Override
public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
receiver.onRequestedOverrideConfigurationChanged(supplier.getConfiguration());
}
};
supplier.registerConfigurationChangeListener(listener);
receiver.registerWindowContainerListener(new WindowContainerListener() {
@Override
public void onRemoved() {
receiver.unregisterWindowContainerListener(this);
supplier.unregisterConfigurationChangeListener(listener);
}
});
}
/**
* Returns the {@link WindowManager.LayoutParams.WindowType}.
*/

View File

@@ -1769,9 +1769,8 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
final ActivityRecord tokenActivity = token.asActivityRecord();
if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) {
tokenActivity.mStartingWindow = win;
if (type == TYPE_APPLICATION_STARTING && activity != null) {
activity.attachStartingWindow(win);
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
activity, win);
}

View File

@@ -4736,7 +4736,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final int drawState = mWinAnimator.mDrawState;
if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
if (mAttrs.type != TYPE_APPLICATION_STARTING) {
mActivityRecord.onFirstWindowDrawn(this, mWinAnimator);
mActivityRecord.onFirstWindowDrawn(this);
} else {
mActivityRecord.onStartingWindowDrawn();
}

View File

@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
@@ -145,6 +146,7 @@ import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import java.util.ArrayList;
import java.util.function.BiConsumer;
/**
@@ -2506,7 +2508,7 @@ public class ActivityRecordTests extends WindowTestsBase {
false, false);
waitUntilHandlersIdle();
activity2.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
true, true, false, true, false, false);
waitUntilHandlersIdle();
assertNoStartingWindow(activity1);
@@ -2523,7 +2525,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// Surprise, ...! Transfer window in the middle of the creation flow.
activity2.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0,
activity1.appToken.asBinder(), true, true, false,
activity1, true, true, false,
true, false, false);
});
activity1.addStartingWindow(mPackageName,
@@ -2544,7 +2546,7 @@ public class ActivityRecordTests extends WindowTestsBase {
false, false);
waitUntilHandlersIdle();
activity2.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
true, true, false, true, false, false);
waitUntilHandlersIdle();
assertNoStartingWindow(activity1);
@@ -2650,7 +2652,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// Make sure the fixed rotation transform linked to activity2 when adding starting window
// on activity2.
topActivity.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity.appToken.asBinder(),
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity,
false, false, false, true, false, false);
waitUntilHandlersIdle();
assertTrue(topActivity.hasFixedRotationTransform());
@@ -2681,6 +2683,52 @@ public class ActivityRecordTests extends WindowTestsBase {
assertHasStartingWindow(activityBottom);
}
@Test
public void testStartingWindowInTaskFragment() {
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
final WindowState startingWindow = createWindowState(
new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING), activity1);
activity1.addWindow(startingWindow);
activity1.attachStartingWindow(startingWindow);
activity1.mStartingData = mock(StartingData.class);
final Task task = activity1.getTask();
final Rect taskBounds = task.getBounds();
final int width = taskBounds.width();
final int height = taskBounds.height();
final BiConsumer<TaskFragment, Rect> fragmentSetup = (fragment, bounds) -> {
final Configuration config = fragment.getRequestedOverrideConfiguration();
config.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
config.windowConfiguration.setBounds(bounds);
fragment.onRequestedOverrideConfigurationChanged(config);
};
final TaskFragment taskFragment1 = new TaskFragment(
mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
fragmentSetup.accept(taskFragment1, new Rect(0, 0, width / 2, height));
task.addChild(taskFragment1, POSITION_TOP);
final TaskFragment taskFragment2 = new TaskFragment(
mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
fragmentSetup.accept(taskFragment2, new Rect(width / 2, 0, width, height));
task.addChild(taskFragment2, POSITION_TOP);
final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
activity2.mVisibleRequested = true;
taskFragment2.addChild(activity2);
activity1.reparent(taskFragment1, POSITION_TOP);
assertEquals(task, activity1.mStartingData.mAssociatedTask);
assertEquals(taskFragment1.getBounds(), activity1.getBounds());
// The activity was resized by task fragment, but starting window must still cover the task.
assertEquals(taskBounds, activity1.mStartingWindow.getBounds());
// The starting window is only removed when all embedded activities are drawn.
final WindowState activityWindow = mock(WindowState.class);
activity1.onFirstWindowDrawn(activityWindow);
assertNotNull(activity1.mStartingWindow);
activity2.onFirstWindowDrawn(activityWindow);
assertNull(activity1.mStartingWindow);
}
@Test
public void testTransitionAnimationBounds() {
removeGlobalMinSizeRestriction();