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
This commit is contained in:
Wale Ogunwale
2019-10-13 23:00:40 +02:00
parent 5acd00dae2
commit c17418e0ae
17 changed files with 258 additions and 270 deletions

View File

@@ -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;
}

View File

@@ -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",

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -540,7 +540,7 @@ public abstract class ConfigurationContainer<E extends 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<E extends ConfigurationContainer> {
listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration);
}
public void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
mChangeListeners.remove(listener);
}
@@ -560,13 +560,12 @@ public abstract class ConfigurationContainer<E extends 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();
}

View File

@@ -4359,9 +4359,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
@Override
void onParentChanged() {
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
if (getParent() != null) {
super.onParentChanged(() -> {
super.onParentChanged(newParent, oldParent, () -> {
mAppAnimationLayer = makeChildSurface(null)
.setName("animationLayer")
.build();
@@ -4381,7 +4381,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
.show(mSplitScreenDividerAnchor);
});
} else {
super.onParentChanged();
super.onParentChanged(newParent, oldParent);
mWmService.mTransactionFactory.get()
.remove(mAppAnimationLayer)
.remove(mBoostedAppAnimationLayer)
@@ -4611,7 +4611,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
@Override
void onParentChanged() {
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
// Since we are the top of the SurfaceControl hierarchy here
// we create the root surfaces explicitly rather than chaining
// up as the default implementation in onParentChanged does. So we

View File

@@ -67,7 +67,6 @@ class Task extends WindowContainer<ActivityRecord> 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<ActivityRecord> 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<ActivityRecord> 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<ActivityRecord> 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<ActivityRecord> 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<ActivityRecord> 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<ActivityRecord> 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<ActivityRecord> 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<ActivityRecord> 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<ActivityRecord> 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());

View File

@@ -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;
}
/**

View File

@@ -950,8 +950,8 @@ public class TaskStack extends WindowContainer<Task> implements
}
@Override
void onParentChanged() {
super.onParentChanged();
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
super.onParentChanged(newParent, oldParent);
if (getParent() != null || mDisplayContent == null) {
return;

View File

@@ -106,6 +106,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
private WindowContainer<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<E> mChildren = new WindowList<E>();
@@ -187,9 +191,45 @@ class WindowContainer<E extends 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<WindowContainer> parent) {
final WindowContainer oldParent = mParent;
mParent = parent;
onParentChanged();
if (!mReparenting) {
onParentChanged(mParent, oldParent);
}
}
/**
@@ -197,12 +237,13 @@ class WindowContainer<E extends 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;
}

View File

@@ -868,8 +868,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
@Override
void onParentChanged() {
super.onParentChanged();
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
super.onParentChanged(newParent, oldParent);
setDrawnStateEvaluated(false /*evaluated*/);
getDisplayContent().reapplyMagnificationSpec();

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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());

View File

@@ -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

View File

@@ -772,7 +772,7 @@ public class WindowContainerTests extends WindowTestsBase {
}
@Override
void onParentChanged() {
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
mOnParentChangedCalled = true;
}