From c17418e0aed2a95681a2a8a4cff13eec6caf3b7e Mon Sep 17 00:00:00 2001 From: Wale Ogunwale Date: Sun, 13 Oct 2019 23:00:40 +0200 Subject: [PATCH] Clean-up ActivityRecord reparenting/adding to Task (57/n) - Updated Configuration#onParentChanged to take params newParent and oldParent to make logic of reparenting less complex to deal with in classes that extend. - Introduced WindowContainer#reparent to consolidate reparenting logic and also allow us to only set onParentChanged once for the operation. - Simplfied logic flow around adding/remove/reparenting activity to task by using methods like addChild(), onChildAdded(), and such. - Removed Task.mDeferRemoval which no longer makes sense and was leading to task and stack leaks. Bug: 80414790 Test: Existing tests pass Change-Id: I4ffa79a1c731dc137213bdd3d7f04b8f013decc4 --- .../android/server/windowmanagerservice.proto | 3 +- data/etc/services.core.protolog.json | 30 +-- .../com/android/server/wm/ActivityRecord.java | 229 ++++++------------ .../com/android/server/wm/ActivityStack.java | 21 +- .../android/server/wm/ActivityStarter.java | 7 +- .../server/wm/ConfigurationContainer.java | 11 +- .../com/android/server/wm/DisplayContent.java | 8 +- .../core/java/com/android/server/wm/Task.java | 38 +-- .../com/android/server/wm/TaskRecord.java | 98 ++++---- .../java/com/android/server/wm/TaskStack.java | 4 +- .../android/server/wm/WindowContainer.java | 51 +++- .../com/android/server/wm/WindowState.java | 4 +- .../server/wm/ActivityRecordTests.java | 6 +- .../server/wm/ActivityStarterTests.java | 2 +- .../server/wm/AppWindowTokenTests.java | 8 + .../wm/ConfigurationContainerTests.java | 6 +- .../server/wm/WindowContainerTests.java | 2 +- 17 files changed, 258 insertions(+), 270 deletions(-) diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index fd1050393de14..a346a63841fe0 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -216,7 +216,8 @@ message TaskProto { optional bool fills_parent = 4; optional .android.graphics.RectProto bounds = 5; optional .android.graphics.RectProto displayed_bounds = 6; - optional bool defer_removal = 7; + // Will be removed soon. + optional bool defer_removal = 7 [deprecated=true]; optional int32 surface_width = 8; optional int32 surface_height = 9; } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 012ffcc28e566..342259d18b055 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -817,12 +817,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, - "-415912575": { - "message": "setTask: %s at top.", - "level": "VERBOSE", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "-415865166": { "message": "findFocusedWindow: Found new focus @ %s", "level": "VERBOSE", @@ -883,6 +877,12 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-303497363": { + "message": "reparent: moving activity=%s to task=%d at %d", + "level": "INFO", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-253016819": { "message": "applyAnimation: transition animation is disabled or skipped. atoken=%s", "level": "VERBOSE", @@ -1237,12 +1237,6 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "393054329": { - "message": "reParentWindowToken: removing window token=%s from task=%s", - "level": "INFO", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "399841913": { "message": "SURFACE RECOVER DESTROY: %s", "level": "INFO", @@ -1351,6 +1345,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/Session.java" }, + "609651209": { + "message": "addChild: %s at top.", + "level": "VERBOSE", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/TaskRecord.java" + }, "620368427": { "message": "******* TELLING SURFACE FLINGER WE ARE BOOTED!", "level": "INFO", @@ -1999,12 +1999,6 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowAnimator.java" }, - "1995048598": { - "message": "reparent: moving app token=%s to task=%d at %d", - "level": "INFO", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "2016061474": { "message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d0af6936e2c6c..0d9570697dbfd 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -137,10 +137,8 @@ import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; import static com.android.server.wm.ActivityStack.ActivityState.STARTED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; -import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; -import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; @@ -299,10 +297,10 @@ import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.animation.Animation; -import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.ReferrerIntent; +import com.android.internal.R; import com.android.internal.util.ToBooleanFunction; import com.android.internal.util.XmlUtils; import com.android.server.AttributeCache; @@ -426,8 +424,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Last configuration reported to the activity in the client process. private MergedConfiguration mLastReportedConfiguration; private int mLastReportedDisplayId; - private boolean mLastReportedMultiWindowMode; - private boolean mLastReportedPictureInPictureMode; + boolean mLastReportedMultiWindowMode; + boolean mLastReportedPictureInPictureMode; CompatibilityInfo compat;// last used compatibility mode ActivityRecord resultTo; // who started this entry, so will get our reply final String resultWho; // additional identifier for use by resultTo. @@ -490,7 +488,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A long lastLaunchTime; // time of last launch of this activity ComponentName requestedVrComponent; // the requested component for handling VR mode. - private boolean inHistory; // are we in the history stack? + boolean inHistory; // are we in the history stack? final ActivityStackSupervisor mStackSupervisor; final RootActivityContainer mRootActivityContainer; @@ -530,10 +528,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // True if we are current in the process of removing this app token from the display private boolean mRemovingFromDisplay = false; - // Flag set while reparenting to prevent actions normally triggered by an individual parent - // change. - private boolean mReparenting; - private RemoteAnimationDefinition mRemoteAnimationDefinition; private AnimatingActivityRegistry mAnimatingActivityRegistry; @@ -1189,7 +1183,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - // TODO: Remove once TaskRecord and Task are unified. + // TODO(task-unify): Remove once TaskRecord and Task are unified. TaskRecord getTaskRecord() { return task; } @@ -1201,16 +1195,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * {@link ActivityStack}. * @param task The new parent {@link TaskRecord}. */ + // TODO(task-unify): Can be remove after task level unification. Callers can just use addChild void setTask(TaskRecord task) { - setTask(task /* task */, false /* reparenting */); - } - - /** - * This method should only be called by {@link TaskRecord#removeActivity(ActivityRecord)}. - * @param task The new parent task. - * @param reparenting Whether we're in the middle of reparenting. - */ - void setTask(TaskRecord task, boolean reparenting) { // Do nothing if the {@link TaskRecord} is the same as the current {@link getTaskRecord}. if (task != null && task == getTaskRecord()) { return; @@ -1222,7 +1208,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Inform old stack (if present) of activity removal and new stack (if set) of activity // addition. if (oldStack != newStack) { - if (!reparenting && oldStack != null) { + if (oldStack != null) { oldStack.onActivityRemovedFromStack(this); } @@ -1231,38 +1217,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + final TaskRecord oldTask = this.task; this.task = task; // This is attaching the activity to the task which we only want to do once. - // TODO: Need to re-work after unifying the task level since it will already have a parent - // then. Just need to restructure the re-parent case not to do this. NOTE that the - // reparenting flag passed in can't be used directly for this as it isn't set in + // TODO(task-unify): Need to re-work after unifying the task level since it will already + // have a parent then. Just need to restructure the re-parent case not to do this. NOTE that + // the reparenting flag passed in can't be used directly for this as it isn't set in // ActivityRecord#reparent() case that ends up calling this method. if (task != null && getParent() == null) { - inHistory = true; - final Task container = task.getTask(); - if (container != null) { - onAttachToTask(task.voiceSession != null, container.getDisplayContent(), - getInputDispatchingTimeoutLocked(this) * 1000000L); - ProtoLog.v(WM_DEBUG_ADD_REMOVE, "setTask: %s at top.", this); - container.addChild(this, Integer.MAX_VALUE /* add on top */); - } - - // TODO(b/36505427): Maybe this call should be moved inside - // updateOverrideConfiguration() - task.updateOverrideConfigurationFromLaunchBounds(); - // Make sure size-compat is up-to-date before using to create window controller. - updateSizeCompatMode(); - - task.addActivityToTop(this); - - // When an activity is started directly into a split-screen fullscreen stack, we need to - // update the initial multi-window modes so that the callbacks are scheduled correctly - // when the user leaves that mode. - mLastReportedMultiWindowMode = inMultiWindowMode(); - mLastReportedPictureInPictureMode = inPinnedWindowingMode(); - } else if (!reparenting) { - onParentChanged(); + task.addChild(this); + } else { + onParentChanged(task, oldTask); } } @@ -1288,24 +1254,46 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } @Override - void onParentChanged() { - super.onParentChanged(); + void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { + final TaskRecord oldTask = (oldParent != null) ? ((Task) oldParent).mTaskRecord : null; + final TaskRecord newTask = (newParent != null) ? ((Task) newParent).mTaskRecord : null; + this.task = newTask; + + super.onParentChanged(newParent, oldParent); final Task task = getTask(); + if (oldParent == null && newParent != null) { + // First time we are adding the activity to the system. + // TODO(task-unify): See if mVoiceInteraction variable is really needed after task level + // unification. + mVoiceInteraction = task.mTaskRecord != null && task.mTaskRecord.voiceSession != null; + mInputDispatchingTimeoutNanos = getInputDispatchingTimeoutLocked(this) * 1000000L; + onDisplayChanged(task.getDisplayContent()); + if (task.mTaskRecord != null) { + task.mTaskRecord.updateOverrideConfigurationFromLaunchBounds(); + } + // Make sure override configuration is up-to-date before using to create window + // controller. + updateSizeCompatMode(); + // When an activity is started directly into a split-screen fullscreen stack, we need to + // update the initial multi-window modes so that the callbacks are scheduled correctly + // when the user leaves that mode. + mLastReportedMultiWindowMode = inMultiWindowMode(); + mLastReportedPictureInPictureMode = inPinnedWindowingMode(); + } + // When the associated task is {@code null}, the {@link ActivityRecord} can no longer // access visual elements like the {@link DisplayContent}. We must remove any associations // such as animations. - if (!mReparenting) { - if (task == null) { - // It is possible we have been marked as a closing app earlier. We must remove ourselves - // from this list so we do not participate in any future animations. - if (getDisplayContent() != null) { - getDisplayContent().mClosingApps.remove(this); - } - } else if (mLastParent != null && mLastParent.mStack != null) { - task.mStack.mExitingActivities.remove(this); + if (task == null) { + // It is possible we have been marked as a closing app earlier. We must remove ourselves + // from this list so we do not participate in any future animations. + if (getDisplayContent() != null) { + getDisplayContent().mClosingApps.remove(this); } + } else if (mLastParent != null && mLastParent.mStack != null) { + task.mStack.mExitingActivities.remove(this); } final TaskStack stack = getStack(); @@ -1320,6 +1308,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mLastParent = task; updateColorTransform(); + + final ActivityStack oldStack = (oldTask != null) ? oldTask.getStack() : null; + final ActivityStack newStack = (newTask != null) ? newTask.getStack() : null; + // Inform old stack (if present) of activity removal and new stack (if set) of activity + // addition. + if (oldStack != newStack) { + // TODO(task-unify): Might be better to use onChildAdded and onChildRemoved signal for + // this once task level is unified. + if (oldStack != null) { + oldStack.onActivityRemovedFromStack(this); + } + if (newStack != null) { + newStack.onActivityAddedToStack(this); + } + } } private void updateColorTransform() { @@ -1697,17 +1700,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return hasProcess() && app.hasThread(); } - void onAttachToTask(boolean voiceInteraction, DisplayContent dc, - long inputDispatchingTimeoutNanos) { - mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; - mVoiceInteraction = voiceInteraction; - onDisplayChanged(dc); - - // Application tokens start out hidden. - setHidden(true); - hiddenRequested = true; - } - boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning, @@ -1967,12 +1959,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A }); } - void removeWindowContainer() { + private void removeAppTokenFromDisplay() { if (mWmService.mRoot == null) return; final DisplayContent dc = mWmService.mRoot.getDisplayContent(getDisplayId()); if (dc == null) { - Slog.w(TAG, "removeWindowContainer: Attempted to remove token: " + Slog.w(TAG, "removeAppTokenFromDisplay: Attempted to remove token: " + appToken + " from non-existing displayId=" + getDisplayId()); return; } @@ -2005,56 +1997,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A + " r=" + this + " (" + prevTask.getStackId() + ")"); } - final Task task = newTask.getTask(); - ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reparent: moving app token=%s" + ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reparent: moving activity=%s" + " to task=%d at %d", this, task.mTaskId, position); - if (task == null) { - throw new IllegalArgumentException("reparent: could not find task"); - } - final Task currentTask = getTask(); - if (task == currentTask) { - throw new IllegalArgumentException( - "window token=" + this + " already child of task=" + currentTask); - } + reparent(newTask.getTask(), position); + } - if (currentTask.mStack != task.mStack) { - throw new IllegalArgumentException( - "window token=" + this + " current task=" + currentTask - + " belongs to a different stack than " + task); - } - - ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reParentWindowToken: removing window token=%s" - + " from task=%s" , this, currentTask); - final DisplayContent prevDisplayContent = getDisplayContent(); - - mReparenting = true; - - getParent().removeChild(this); - task.addChild(this, position); - - mReparenting = false; - - // Relayout display(s). - final DisplayContent displayContent = task.getDisplayContent(); - displayContent.setLayoutNeeded(); - if (prevDisplayContent != displayContent) { - onDisplayChanged(displayContent); - prevDisplayContent.setLayoutNeeded(); - } - getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); - - // Reparenting prevents informing the parent stack of activity removal in the case that - // the new stack has the same parent. we must manually signal here if this is not the case. - final ActivityStack prevStack = prevTask.getStack(); - - if (prevStack != newTask.getStack()) { - prevStack.onActivityRemovedFromStack(this); - } - // Remove the activity from the old task and add it to the new task. - prevTask.removeActivity(this, true /* reparenting */); - - newTask.addActivityAtIndex(position, this); + // TODO(task-unify): Remove once Task level is unified. + void onParentChanged(TaskRecord newParent, TaskRecord oldParent) { + onParentChanged( + newParent != null ? newParent.mTask : null, + oldParent != null ? oldParent.mTask : null); } private boolean isHomeIntent(Intent intent) { @@ -2895,6 +2848,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** Note: call {@link #cleanUp(boolean, boolean)} before this method. */ + // TODO(task-unify): Look into consolidating this with TaskRecord.removeChild once we unify + // task level. void removeFromHistory(String reason) { finishActivityResults(Activity.RESULT_CANCELED, null /* resultData */); makeFinishingLocked(); @@ -2912,41 +2867,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A setState(DESTROYED, "removeFromHistory"); if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + this); app = null; - removeWindowContainer(); - final TaskRecord task = getTaskRecord(); - final boolean lastActivity = task.removeActivity(this); - // If we are removing the last activity in the task, not including task overlay activities, - // then fall through into the block below to remove the entire task itself - final boolean onlyHasTaskOverlays = - task.onlyHasTaskOverlayActivities(false /* excludingFinishing */); - - if (lastActivity || onlyHasTaskOverlays) { - if (DEBUG_STATES) { - Slog.i(TAG, "removeFromHistory: last activity removed from " + this - + " onlyHasTaskOverlays=" + onlyHasTaskOverlays); - } - - // The following block can be executed multiple times if there is more than one overlay. - // {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup - // of the task by id and exiting early if not found. - if (onlyHasTaskOverlays) { - // When destroying a task, tell the supervisor to remove it so that any activity it - // has can be cleaned up correctly. This is currently the only place where we remove - // a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays - // state into removeTask(), we just clear the task here before the other residual - // work. - // TODO: If the callers to removeTask() changes such that we have multiple places - // where we are destroying the task, move this back into removeTask() - mStackSupervisor.removeTaskByIdLocked(task.mTaskId, false /* killProcess */, - !REMOVE_FROM_RECENTS, reason); - } - - // We must keep the task around until all activities are destroyed. The following - // statement will only execute once since overlays are also considered activities. - if (lastActivity) { - stack.removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING); - } - } + removeAppTokenFromDisplay(); cleanUpActivityServices(); removeUriPermissionsLocked(); diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 41c1e4e7fcc52..a2353a0226310 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -246,12 +246,13 @@ class ActivityStack extends ConfigurationContainer { ActivityDisplay current = getParent(); if (current != parent) { mDisplayId = parent.mDisplayId; - onParentChanged(); + onParentChanged(parent, current); } } @Override - protected void onParentChanged() { + protected void onParentChanged( + ConfigurationContainer newParent, ConfigurationContainer oldParent) { ActivityDisplay display = getParent(); if (display != null) { // Rotations are relative to the display. This means if there are 2 displays rotated @@ -264,7 +265,7 @@ class ActivityStack extends ConfigurationContainer { getConfiguration().windowConfiguration.setRotation( display.getWindowConfiguration().getRotation()); } - super.onParentChanged(); + super.onParentChanged(newParent, oldParent); if (display != null && inSplitScreenPrimaryWindowingMode()) { // If we created a docked stack we want to resize it so it resizes all other stacks // in the system. @@ -915,12 +916,13 @@ class ActivityStack extends ConfigurationContainer { /** Removes the stack completely. Also calls WindowManager to do the same on its side. */ void remove() { + final ActivityDisplay oldDisplay = getDisplay(); removeFromDisplay(); if (mTaskStack != null) { mTaskStack.removeIfPossible(); mTaskStack = null; } - onParentChanged(); + onParentChanged(null, oldDisplay); } ActivityDisplay getDisplay() { @@ -4719,12 +4721,13 @@ class ActivityStack extends ConfigurationContainer { * {@link #REMOVE_TASK_MODE_MOVING}, {@link #REMOVE_TASK_MODE_MOVING_TO_TOP}. */ void removeTask(TaskRecord task, String reason, int mode) { - final boolean removed = mTaskHistory.remove(task); - - if (removed) { - EventLog.writeEvent(EventLogTags.AM_REMOVE_TASK, task.mTaskId, getStackId()); + if (!mTaskHistory.remove(task)) { + // Not really in this stack anymore... + return; } + EventLog.writeEvent(EventLogTags.AM_REMOVE_TASK, task.mTaskId, getStackId()); + removeActivitiesFromLRUList(task); updateTaskMovement(task, true); @@ -4758,7 +4761,7 @@ class ActivityStack extends ConfigurationContainer { if (inPinnedWindowingMode()) { mService.getTaskChangeNotificationController().notifyActivityUnpinned(); } - if (display.isSingleTaskInstance()) { + if (display != null && display.isSingleTaskInstance()) { mService.notifySingleTaskDisplayEmpty(display.mDisplayId); } } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 2ac681caa765d..5225edcdfb3b8 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1864,9 +1864,8 @@ class ActivityStarter { mLaunchFlags); // The above code can remove {@code reusedActivity} from the task, leading to the - // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The - // task reference is needed in the call below to - // {@link setTargetStackAndMoveToFrontIfNeeded}. + // {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The task + // reference is needed in the call below to {@link setTargetStackAndMoveToFrontIfNeeded} if (targetTaskTop.getTaskRecord() == null) { targetTaskTop.setTask(targetTask); } @@ -2450,7 +2449,7 @@ class ActivityStarter { private void addOrReparentStartingActivity(TaskRecord parent, String reason) { if (mStartActivity.getTaskRecord() == null || mStartActivity.getTaskRecord() == parent) { - parent.addActivityToTop(mStartActivity); + parent.addChild(mStartActivity); } else { mStartActivity.reparent(parent, parent.getChildCount() /* top */, reason); } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 300ee1ddc77dc..70d5ab93421e2 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -540,7 +540,7 @@ public abstract class ConfigurationContainer { return sameWindowingMode; } - public void registerConfigurationChangeListener(ConfigurationContainerListener listener) { + void registerConfigurationChangeListener(ConfigurationContainerListener listener) { if (mChangeListeners.contains(listener)) { return; } @@ -548,7 +548,7 @@ public abstract class ConfigurationContainer { listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration); } - public void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) { + void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) { mChangeListeners.remove(listener); } @@ -560,13 +560,12 @@ public abstract class ConfigurationContainer { /** * Must be called when new parent for the container was set. */ - void onParentChanged() { - final ConfigurationContainer parent = getParent(); + void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { // Removing parent usually means that we've detached this entity to destroy it or to attach // to another parent. In both cases we don't need to update the configuration now. - if (parent != null) { + if (newParent != null) { // Update full configuration of this container and all its children. - onConfigurationChanged(parent.mFullConfiguration); + onConfigurationChanged(newParent.mFullConfiguration); // Update merged override configuration of this container and all its children. onMergedOverrideConfigurationChanged(); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index aa0b68b9bfc56..2d1d29710c95a 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4359,9 +4359,9 @@ class DisplayContent extends WindowContainer { + super.onParentChanged(newParent, oldParent, () -> { mAppAnimationLayer = makeChildSurface(null) .setName("animationLayer") .build(); @@ -4381,7 +4381,7 @@ class DisplayContent extends WindowContainer implements ConfigurationConta final int mTaskId; /* User for which this task was created. */ final int mUserId; - private boolean mDeferRemoval = false; final Rect mPreparedFrozenBounds = new Rect(); final Configuration mPreparedFrozenMergedConfig = new Configuration(); @@ -176,14 +175,18 @@ class Task extends WindowContainer implements ConfigurationConta void addChild(ActivityRecord child, int position) { position = getAdjustedAddPosition(position); super.addChild(child, position); - mDeferRemoval = false; + + // Inform the TaskRecord side of the child addition + // TODO(task-unify): Will be removed after task unification. + if (mTaskRecord != null) { + mTaskRecord.onChildAdded(child, position); + } } @Override void positionChildAt(int position, ActivityRecord child, boolean includingParents) { position = getAdjustedAddPosition(position); super.positionChildAt(position, child, includingParents); - mDeferRemoval = false; } private boolean hasWindowsAlive() { @@ -197,8 +200,10 @@ class Task extends WindowContainer implements ConfigurationConta @VisibleForTesting boolean shouldDeferRemoval() { - // TODO: This should probably return false if mChildren.isEmpty() regardless if the stack - // is animating... + if (mChildren.isEmpty()) { + // No reason to defer removal of a Task that doesn't have any child. + return false; + } return hasWindowsAlive() && mStack.isSelfOrChildAnimating(); } @@ -206,7 +211,6 @@ class Task extends WindowContainer implements ConfigurationConta void removeIfPossible() { if (shouldDeferRemoval()) { if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId); - mDeferRemoval = true; return; } removeImmediately(); @@ -216,7 +220,6 @@ class Task extends WindowContainer implements ConfigurationConta void removeImmediately() { if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId); EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeTask"); - mDeferRemoval = false; if (mTaskRecord != null) { mTaskRecord.unregisterConfigurationChangeListener(this); } @@ -266,8 +269,8 @@ class Task extends WindowContainer implements ConfigurationConta } @Override - void onParentChanged() { - super.onParentChanged(); + void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { + super.onParentChanged(newParent, oldParent); // Update task bounds if needed. adjustBoundsForDisplayChangeIfNeeded(getDisplayContent()); @@ -290,11 +293,18 @@ class Task extends WindowContainer implements ConfigurationConta super.removeChild(child); + // Inform the TaskRecord side of the child removal + // TODO(task-unify): Will be removed after task unification. + if (mTaskRecord != null) { + mTaskRecord.onChildRemoved(child); + } + + // TODO(task-unify): Need to make this account for what we are doing in + // ActivityRecord.removeFromHistory so that the task isn't removed in some situations when + // we unify task level. if (mChildren.isEmpty()) { EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeActivity: last activity"); - if (mDeferRemoval) { - removeIfPossible(); - } + removeIfPossible(); } } @@ -745,7 +755,7 @@ class Task extends WindowContainer implements ConfigurationConta @Override public String toString() { - return "{taskId=" + mTaskId + " appTokens=" + mChildren + " mdr=" + mDeferRemoval + "}"; + return "{taskId=" + mTaskId + " appTokens=" + mChildren + "}"; } String getName() { @@ -792,7 +802,6 @@ class Task extends WindowContainer implements ConfigurationConta proto.write(FILLS_PARENT, matchParentBounds()); getBounds().writeToProto(proto, BOUNDS); mOverrideDisplayedBounds.writeToProto(proto, DISPLAYED_BOUNDS); - proto.write(DEFER_REMOVAL, mDeferRemoval); proto.write(SURFACE_WIDTH, mSurfaceControl.getWidth()); proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight()); proto.end(token); @@ -805,7 +814,6 @@ class Task extends WindowContainer implements ConfigurationConta pw.println(prefix + "taskId=" + mTaskId); pw.println(doublePrefix + "mBounds=" + getBounds().toShortString()); - pw.println(doublePrefix + "mdr=" + mDeferRemoval); pw.println(doublePrefix + "appTokens=" + mChildren); pw.println(doublePrefix + "mDisplayedBounds=" + mOverrideDisplayedBounds.toShortString()); diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 89499d695dd06..166bd058b080c 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -68,13 +68,16 @@ import static com.android.server.am.TaskRecordProto.RESIZE_MODE; import static com.android.server.am.TaskRecordProto.STACK_ID; import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED; import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN; +import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING_TO_TOP; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; @@ -82,6 +85,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECEN import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS; 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.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; @@ -126,6 +130,7 @@ import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.util.XmlUtils; +import com.android.server.protolog.common.ProtoLog; import com.android.server.wm.ActivityStack.ActivityState; import org.xmlpull.v1.XmlPullParser; @@ -969,6 +974,7 @@ class TaskRecord extends ConfigurationContainer { * Must be used for setting parent stack because it performs configuration updates. * Must be called after adding task as a child to the stack. */ + // TODO(task-unify): Remove or rework after task level unification. void setStack(ActivityStack stack) { if (stack != null && !stack.isInStackLocked(this)) { throw new IllegalStateException("Task must be added as a Stack child first."); @@ -993,7 +999,7 @@ class TaskRecord extends ConfigurationContainer { } } - onParentChanged(); + onParentChanged(mStack, oldStack); } /** @@ -1019,8 +1025,9 @@ class TaskRecord extends ConfigurationContainer { } @Override - protected void onParentChanged() { - super.onParentChanged(); + protected void onParentChanged( + ConfigurationContainer newParent, ConfigurationContainer oldParent) { + super.onParentChanged(newParent, oldParent); mAtmService.mRootActivityContainer.updateUIDsPresentOnDisplay(); } @@ -1215,10 +1222,6 @@ class TaskRecord extends ConfigurationContainer { updateEffectiveIntent(); } - void addActivityToTop(ActivityRecord r) { - addActivityAtIndex(getChildCount(), r); - } - @Override /*@WindowConfiguration.ActivityType*/ public int getActivityType() { @@ -1229,18 +1232,11 @@ class TaskRecord extends ConfigurationContainer { return getChildAt(0).getActivityType(); } - /** - * Adds an activity {@param r} at the given {@param index}. The activity {@param r} must either - * be in the current task or unparented to any task. - */ - void addActivityAtIndex(int index, ActivityRecord r) { - TaskRecord task = r.getTaskRecord(); - if (task != null && task != this) { - throw new IllegalArgumentException("Can not add r=" + " to task=" + this - + " current parent=" + task); - } - - r.setTask(this); + /** Called when a Task child is added from the Task.java side. */ + // TODO(task-unify): Just override addChild to do what is needed when someone calls to add a + // child. + void onChildAdded(ActivityRecord r, int index) { + r.inHistory = true; // Remove r first, and if it wasn't already in the list and it's fullscreen, count it. if (!mActivities.remove(r) && r.occludesParent()) { @@ -1287,34 +1283,34 @@ class TaskRecord extends ConfigurationContainer { mAtmService.notifyTaskPersisterLocked(this, false); } - if (r.getParent() != null) { - // Only attempt to move in WM if the child has a controller. It is possible we haven't - // created controller for the activity we are starting yet. - mTask.positionChildAt(r, index); - } - // Make sure the list of display UID whitelists is updated // now that this record is in a new task. mAtmService.mRootActivityContainer.updateUIDsPresentOnDisplay(); } - /** - * Removes the specified activity from this task. - * @param r The {@link ActivityRecord} to remove. - * @return true if this was the last activity in the task. - */ - boolean removeActivity(ActivityRecord r) { - return removeActivity(r, false /* reparenting */); - } - - boolean removeActivity(ActivityRecord r, boolean reparenting) { - if (r.getTaskRecord() != this) { - throw new IllegalArgumentException( - "Activity=" + r + " does not belong to task=" + this); + // TODO(task-unify): Merge onChildAdded method below into this since task will be a single + // object. + void addChild(ActivityRecord r) { + if (r.getParent() != null) { + // Shouldn't already have a parent since we are just adding to the task... + throw new IllegalStateException( + "r=" + r + " parent=" + r.getParent() + " task=" + this); } - r.setTask(null /* task */, reparenting /* reparenting */); + ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addChild: %s at top.", this); + // This means the activity isn't attached to Task.java yet. Go ahead and do that. + // TODO(task-unify): Remove/call super once we unify task level. + if (mTask != null) { + mTask.addChild(r, Integer.MAX_VALUE /* add on top */); + } else { + onChildAdded(r, Integer.MAX_VALUE); + } + } + /** Called when a Task child is removed from the Task.java side. */ + // TODO(task-unify): Just override removeChild to do what is needed when someone calls to remove + // a child. + void onChildRemoved(ActivityRecord r) { if (mActivities.remove(r) && r.occludesParent()) { // Was previously in list. numFullscreen--; @@ -1330,11 +1326,27 @@ class TaskRecord extends ConfigurationContainer { mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged(); } - if (!hasChild()) { - return !mReuseTask; + if (hasChild()) { + updateEffectiveIntent(); + + // The following block can be executed multiple times if there is more than one overlay. + // {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup + // of the task by id and exiting early if not found. + if (onlyHasTaskOverlayActivities(false /* excludingFinishing */)) { + // When destroying a task, tell the supervisor to remove it so that any activity it + // has can be cleaned up correctly. This is currently the only place where we remove + // a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays + // state into removeTask(), we just clear the task here before the other residual + // work. + // TODO: If the callers to removeTask() changes such that we have multiple places + // where we are destroying the task, move this back into removeTask() + mAtmService.mStackSupervisor.removeTaskByIdLocked(mTaskId, false /* killProcess */, + !REMOVE_FROM_RECENTS, "onChildRemoved"); + } + } else if (!mReuseTask) { + // Remove entire task if it doesn't have any activity left and it isn't marked for reuse + mStack.removeTask(this, "onChildRemoved", REMOVE_TASK_MODE_DESTROYING); } - updateEffectiveIntent(); - return false; } /** diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index fc9a110bb4b19..3552245360664 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -950,8 +950,8 @@ public class TaskStack extends WindowContainer implements } @Override - void onParentChanged() { - super.onParentChanged(); + void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { + super.onParentChanged(newParent, oldParent); if (getParent() != null || mDisplayContent == null) { return; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 037edf1b5ab1e..a620a7cfe42ba 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -106,6 +106,10 @@ class WindowContainer extends ConfigurationContainer< */ private WindowContainer mParent = null; + // Set to true when we are performing a reparenting operation so we only send one + // onParentChanged() notification. + private boolean mReparenting; + // List of children for this window container. List is in z-order as the children appear on // screen with the top-most window container at the tail of the list. protected final WindowList mChildren = new WindowList(); @@ -187,9 +191,45 @@ class WindowContainer extends ConfigurationContainer< scheduleAnimation(); } + void reparent(WindowContainer newParent, int position) { + if (newParent == null) { + throw new IllegalArgumentException("reparent: can't reparent to null " + this); + } + + final WindowContainer oldParent = mParent; + if (mParent == newParent) { + throw new IllegalArgumentException("WC=" + this + " already child of " + mParent); + } + + // The display object before reparenting as that might lead to old parent getting removed + // from the display if it no longer has any child. + final DisplayContent prevDc = oldParent.getDisplayContent(); + final DisplayContent dc = newParent.getDisplayContent(); + + mReparenting = true; + oldParent.removeChild(this); + newParent.addChild(this, position); + mReparenting = false; + + // Send onParentChanged notification here is we disabled sending it in setParent for + // reparenting case. + onParentChanged(newParent, oldParent); + + // Relayout display(s) + dc.setLayoutNeeded(); + if (prevDc != dc) { + onDisplayChanged(dc); + prevDc.setLayoutNeeded(); + } + getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); + } + final protected void setParent(WindowContainer parent) { + final WindowContainer oldParent = mParent; mParent = parent; - onParentChanged(); + if (!mReparenting) { + onParentChanged(mParent, oldParent); + } } /** @@ -197,12 +237,13 @@ class WindowContainer extends ConfigurationContainer< * Supposed to be overridden and contain actions that should be executed after parent was set. */ @Override - void onParentChanged() { - onParentChanged(null); + void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { + onParentChanged(newParent, oldParent, null); } - void onParentChanged(PreAssignChildLayersCallback callback) { - super.onParentChanged(); + void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent, + PreAssignChildLayersCallback callback) { + super.onParentChanged(newParent, oldParent); if (mParent == null) { return; } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 991241dd645f3..f0ea13d2c68a2 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -868,8 +868,8 @@ class WindowState extends WindowContainer implements WindowManagerP } @Override - void onParentChanged() { - super.onParentChanged(); + void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { + super.onParentChanged(newParent, oldParent); setDrawnStateEvaluated(false /*evaluated*/); getDisplayContent().reapplyMagnificationSpec(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index cc78ec7c86c33..aceb6335c4d36 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -130,13 +130,13 @@ public class ActivityRecordTests extends ActivityTestsBase { @Test public void testStackCleanupOnClearingTask() { - mActivity.setTask(null); + mActivity.onParentChanged(null /*newParent*/, mActivity.getTask()); verify(mStack, times(1)).onActivityRemovedFromStack(any()); } @Test public void testStackCleanupOnActivityRemoval() { - mTask.removeActivity(mActivity); + mTask.mTask.removeChild(mActivity); verify(mStack, times(1)).onActivityRemovedFromStack(any()); } @@ -795,7 +795,7 @@ public class ActivityRecordTests extends ActivityTestsBase { // Remove activity from task mActivity.finishing = false; - mActivity.setTask(null); + mActivity.onParentChanged(null /*newParent*/, mActivity.getTask()); assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_CANCELLED, mActivity.finishIfPossible("test", false /* oomAdj */)); assertFalse(mActivity.finishing); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 8a9423a2f72ea..10802b14d966b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -710,7 +710,7 @@ public class ActivityStarterTests extends ActivityTestsBase { if (startedActivity != null && startedActivity.getTaskRecord() != null) { // Remove the activity so it doesn't interfere with with subsequent activity launch // tests from this method. - startedActivity.getTaskRecord().removeActivity(startedActivity); + startedActivity.getTaskRecord().mTask.removeChild(startedActivity); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index d68aef0415ace..2f0486d3e81b6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -35,6 +35,7 @@ import static android.view.WindowManager.TRANSIT_UNSET; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; @@ -208,6 +209,9 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testSizeCompatBounds() { + // Disable the real configuration resolving because we only simulate partial flow. + // TODO: Have test use full flow. + doNothing().when(mTask.mTaskRecord).computeConfigResourceOverrides(any(), any()); final Rect fixedBounds = mActivity.getRequestedOverrideConfiguration().windowConfiguration .getBounds(); fixedBounds.set(0, 0, 1200, 1600); @@ -249,6 +253,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test @Presubmit public void testGetOrientation() { + mActivity.setHidden(false); + mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); mActivity.setOccludesParent(false); @@ -309,6 +315,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testSetOrientation() { + mActivity.setHidden(false); + // Assert orientation is unspecified to start. assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOrientation()); diff --git a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java index e7f7d2106e6bc..bcd9371510464 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java @@ -334,8 +334,9 @@ public class ConfigurationContainerTests { private TestConfigurationContainer mParent; TestConfigurationContainer addChild(TestConfigurationContainer childContainer) { + final ConfigurationContainer oldParent = childContainer.getParent(); childContainer.mParent = this; - childContainer.onParentChanged(); + childContainer.onParentChanged(this, oldParent); mChildren.add(childContainer); return childContainer; } @@ -349,8 +350,9 @@ public class ConfigurationContainerTests { } void removeChild(TestConfigurationContainer child) { + final ConfigurationContainer oldParent = child.getParent(); child.mParent = null; - child.onParentChanged(); + child.onParentChanged(null, oldParent); } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 8117ff601a8fa..85aff7f9a9cd3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -772,7 +772,7 @@ public class WindowContainerTests extends WindowTestsBase { } @Override - void onParentChanged() { + void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { mOnParentChangedCalled = true; }