Merge "Ensure that participated activities are not omitted by ancestor"

This commit is contained in:
TreeHugger Robot
2022-01-25 14:01:33 +00:00
committed by Android (Google) Code Review
4 changed files with 284 additions and 292 deletions

View File

@@ -355,6 +355,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotation.java"
},
"-1728919185": {
"message": " unrelated invisible sibling %s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"-1715268616": {
"message": "Last window, removing starting window %s",
"level": "VERBOSE",
@@ -451,12 +457,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
"-1587921395": {
"message": " Top targets: %s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"-1585311008": {
"message": "Bring to front target: %s from %s",
"level": "DEBUG",
@@ -709,12 +709,6 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
"-1375751630": {
"message": " --- Start combine pass ---",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"-1364754753": {
"message": "Task vanished taskId=%d",
"level": "VERBOSE",
@@ -1177,12 +1171,6 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
"-855366859": {
"message": " merging children in from %s: %s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"-853404763": {
"message": "\twallpaper=%s",
"level": "DEBUG",
@@ -1243,6 +1231,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"-779095785": {
"message": " sibling is a participant with mode %s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"-775004869": {
"message": "Not a match: %s",
"level": "DEBUG",
@@ -1555,12 +1549,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
"-446752714": {
"message": " SKIP: sibling contains top target %s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"-445944810": {
"message": "finish(%b): mCanceled=%b",
"level": "DEBUG",
@@ -1729,12 +1717,6 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
"-302335479": {
"message": " remove from topTargets %s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"-292790591": {
"message": "Attempted to set IME policy to a display that does not exist: %d",
"level": "WARN",
@@ -2077,6 +2059,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
"112145970": {
"message": " SKIP: its sibling was rejected",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"114070759": {
"message": "New wallpaper target: %s prevTarget: %s caller=%s",
"level": "VERBOSE",
@@ -2413,6 +2401,12 @@
"group": "WM_SHOW_SURFACE_ALLOC",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
"405146734": {
"message": " Final targets: %s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"416924848": {
"message": "InsetsSource Control %s for target %s",
"level": "DEBUG",
@@ -2437,12 +2431,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
"430260320": {
"message": " sibling is a top target with mode %s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"431715812": {
"message": "Launch on display check: allow launch any on display",
"level": "DEBUG",
@@ -2761,12 +2749,6 @@
"group": "WM_SHOW_SURFACE_ALLOC",
"at": "com\/android\/server\/wm\/WindowStateAnimator.java"
},
"751854538": {
"message": "DisplayArea keep clear rects changed name =%s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
},
"765395228": {
"message": "onAnimationFinished(): controller=%s reorderMode=%d",
"level": "DEBUG",
@@ -3097,12 +3079,6 @@
"group": "WM_DEBUG_WALLPAPER",
"at": "com\/android\/server\/wm\/WallpaperController.java"
},
"1186730970": {
"message": " no common mode yet, so set it",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"1191587912": {
"message": "Moved rootTask=%s behind rootTask=%s",
"level": "DEBUG",

View File

@@ -37,7 +37,6 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHA
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -72,6 +71,7 @@ import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -156,7 +156,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
final ArraySet<WindowContainer> mParticipants = new ArraySet<>();
/** The final animation targets derived from participants after promotion. */
private ArraySet<WindowContainer> mTargets = null;
private ArrayList<WindowContainer> mTargets;
/** The main display running this transition. */
private DisplayContent mTargetDisplay;
@@ -385,7 +385,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// usually only size 1
final ArraySet<DisplayContent> displays = new ArraySet<>();
for (int i = mTargets.size() - 1; i >= 0; --i) {
final WindowContainer target = mTargets.valueAt(i);
final WindowContainer target = mTargets.get(i);
if (target.getParent() != null) {
final SurfaceControl targetLeash = getLeashSurface(target);
final SurfaceControl origParent = getOrigParentSurface(target);
@@ -818,7 +818,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// Search for the home task. If it is supposed to be visible, then the navbar is not at
// the bottom of the screen, so we need to animate it.
for (int i = 0; i < mTargets.size(); ++i) {
final Task task = mTargets.valueAt(i).asTask();
final Task task = mTargets.get(i).asTask();
if (task == null || !task.isHomeOrRecentsRootTask()) continue;
animate = task.isVisibleRequested();
break;
@@ -901,23 +901,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
private static boolean reportIfNotTop(WindowContainer wc) {
// Organized tasks need to be reported anyways because Core won't show() their surfaces
// and we can't rely on onTaskAppeared because it isn't in sync.
// Also report wallpaper so it can be handled properly during display change/rotation.
// TODO(shell-transitions): switch onTaskAppeared usage over to transitions OPEN.
return wc.isOrganized() || isWallpaper(wc);
}
/** @return the depth of child within ancestor, 0 if child == ancestor, or -1 if not a child. */
private static int getChildDepth(WindowContainer child, WindowContainer ancestor) {
WindowContainer parent = child;
int depth = 0;
while (parent != null) {
if (parent == ancestor) {
return depth;
}
parent = parent.getParent();
++depth;
}
return -1;
return wc.isOrganized();
}
private static boolean isWallpaper(WindowContainer wc) {
@@ -947,61 +932,48 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
*
* @return {@code true} if transition in target can be promoted to its parent.
*/
private static boolean canPromote(WindowContainer target, ArraySet<WindowContainer> topTargets,
private static boolean canPromote(WindowContainer<?> target, Targets targets,
ArrayMap<WindowContainer, ChangeInfo> changes) {
final WindowContainer parent = target.getParent();
final ChangeInfo parentChanges = parent != null ? changes.get(parent) : null;
if (parent == null || !parent.canCreateRemoteAnimationTarget()
|| parentChanges == null || !parentChanges.hasChanged(parent)) {
final WindowContainer<?> parent = target.getParent();
final ChangeInfo parentChange = changes.get(parent);
if (!parent.canCreateRemoteAnimationTarget()
|| parentChange == null || !parentChange.hasChanged(parent)) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " SKIP: %s",
parent == null ? "no parent" : ("parent can't be target " + parent));
"parent can't be target " + parent);
return false;
}
if (isWallpaper(target)) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " SKIP: is wallpaper");
return false;
}
@TransitionInfo.TransitionMode int mode = TRANSIT_NONE;
// Go through all siblings of this target to see if any of them would prevent
// the target from promoting.
siblingLoop:
final @TransitionInfo.TransitionMode int mode = changes.get(target).getTransitMode(target);
for (int i = parent.getChildCount() - 1; i >= 0; --i) {
final WindowContainer sibling = parent.getChildAt(i);
final WindowContainer<?> sibling = parent.getChildAt(i);
if (target == sibling) continue;
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " check sibling %s",
sibling);
// Check if any topTargets are the sibling or within it
for (int j = topTargets.size() - 1; j >= 0; --j) {
final int depth = getChildDepth(topTargets.valueAt(j), sibling);
if (depth < 0) continue;
if (depth == 0) {
final int siblingMode = changes.get(sibling).getTransitMode(sibling);
final ChangeInfo siblingChange = changes.get(sibling);
if (siblingChange == null || !targets.wasParticipated(sibling)) {
if (sibling.isVisibleRequested()) {
// Sibling is visible but not animating, so no promote.
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" sibling is a top target with mode %s",
TransitionInfo.modeToString(siblingMode));
if (mode == TRANSIT_NONE) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" no common mode yet, so set it");
mode = siblingMode;
} else if (mode != siblingMode) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" SKIP: common mode mismatch. was %s",
TransitionInfo.modeToString(mode));
return false;
}
continue siblingLoop;
} else {
// Sibling subtree may not be promotable.
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" SKIP: sibling contains top target %s",
topTargets.valueAt(j));
" SKIP: sibling is visible but not part of transition");
return false;
}
}
// No other animations are playing in this sibling
if (sibling.isVisibleRequested()) {
// Sibling is visible but not animating, so no promote.
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" SKIP: sibling is visible but not part of transition");
" unrelated invisible sibling %s", sibling);
continue;
}
final int siblingMode = siblingChange.getTransitMode(sibling);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" sibling is a participant with mode %s",
TransitionInfo.modeToString(siblingMode));
if (mode != siblingMode) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" SKIP: common mode mismatch. was %s",
TransitionInfo.modeToString(mode));
return false;
}
}
@@ -1011,58 +983,40 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
/**
* Go through topTargets and try to promote (see {@link #canPromote}) one of them.
*
* @param topTargets set of just the top-most targets in the hierarchy of participants.
* @param targets all targets that will be sent to the player.
* @return {@code true} if something was promoted.
*/
private static boolean tryPromote(ArraySet<WindowContainer> topTargets,
ArraySet<WindowContainer> targets, ArrayMap<WindowContainer, ChangeInfo> changes) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " --- Start combine pass ---");
// Go through each target until we find one that can be promoted.
for (WindowContainer targ : topTargets) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " checking %s", targ);
if (!canPromote(targ, topTargets, changes)) {
private static void tryPromote(Targets targets, ArrayMap<WindowContainer, ChangeInfo> changes) {
WindowContainer<?> lastNonPromotableParent = null;
// Go through from the deepest target.
for (int i = targets.mArray.size() - 1; i >= 0; --i) {
final WindowContainer<?> target = targets.mArray.valueAt(i);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " checking %s", target);
final WindowContainer<?> parent = target.getParent();
if (parent == lastNonPromotableParent) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" SKIP: its sibling was rejected");
continue;
}
// No obstructions found to promotion, so promote
final WindowContainer parent = targ.getParent();
final ChangeInfo parentInfo = changes.get(parent);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" CAN PROMOTE: promoting to parent %s", parent);
targets.add(parent);
// Go through all children of newly-promoted container and remove them from the
// top-targets.
for (int i = parent.getChildCount() - 1; i >= 0; --i) {
final WindowContainer child = parent.getChildAt(i);
int idx = targets.indexOf(child);
if (idx >= 0) {
final ChangeInfo childInfo = changes.get(child);
if (reportIfNotTop(child)) {
childInfo.mParent = parent;
parentInfo.addChild(child);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" keep as target %s", child);
} else {
if (childInfo.mChildren != null) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" merging children in from %s: %s", child,
childInfo.mChildren);
parentInfo.addChildren(childInfo.mChildren);
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" remove from targets %s", child);
targets.removeAt(idx);
}
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" remove from topTargets %s", child);
topTargets.remove(child);
if (!canPromote(target, targets, changes)) {
lastNonPromotableParent = parent;
continue;
}
if (reportIfNotTop(target)) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" keep as target %s", target);
} else {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" remove from targets %s", target);
targets.remove(i, target);
}
if (targets.mArray.indexOfValue(parent) < 0) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" CAN PROMOTE: promoting to parent %s", parent);
// The parent has lower depth, so it will be checked in the later iteration.
i++;
targets.add(parent);
}
topTargets.add(parent);
return true;
}
return false;
}
/**
@@ -1071,22 +1025,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
*/
@VisibleForTesting
@NonNull
static ArraySet<WindowContainer> calculateTargets(ArraySet<WindowContainer> participants,
static ArrayList<WindowContainer> calculateTargets(ArraySet<WindowContainer> participants,
ArrayMap<WindowContainer, ChangeInfo> changes) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Start calculating TransitionInfo based on participants: %s", participants);
final ArraySet<WindowContainer> topTargets = new ArraySet<>();
// The final animation targets which cannot promote to higher level anymore.
final ArraySet<WindowContainer> targets = new ArraySet<>();
final ArrayList<WindowContainer> tmpList = new ArrayList<>();
// Build initial set of top-level participants by removing any participants that are no-ops
// or children of other participants or are otherwise invalid; however, keep around a list
// of participants that should always be reported even if they aren't top.
for (WindowContainer wc : participants) {
// Don't include detached windows.
// Add all valid participants to the target container.
final Targets targets = new Targets();
for (int i = participants.size() - 1; i >= 0; --i) {
final WindowContainer<?> wc = participants.valueAt(i);
if (!wc.isAttached()) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" Rejecting as detached: %s", wc);
@@ -1101,70 +1048,62 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
" Rejecting as no-op: %s", wc);
continue;
}
// Search through ancestors to find the top-most participant (if one exists)
WindowContainer topParent = null;
tmpList.clear();
if (reportIfNotTop(wc)) {
tmpList.add(wc);
}
// Wallpaper must be the top (regardless of how nested it is in DisplayAreas).
boolean skipIntermediateReports = isWallpaper(wc);
for (WindowContainer p = wc.getParent(); p != null; p = p.getParent()) {
if (!p.isAttached() || changes.get(p) == null || !changes.get(p).hasChanged(p)) {
// Again, we're skipping no-ops
break;
}
if (participants.contains(p)) {
topParent = p;
break;
} else if (isWallpaper(p)) {
skipIntermediateReports = true;
} else if (reportIfNotTop(p) && !skipIntermediateReports) {
tmpList.add(p);
}
}
if (topParent != null) {
// There was an ancestor participant, so don't add wc to targets unless always-
// report. Similarly, add any always-report parents along the way.
for (int i = 0; i < tmpList.size(); ++i) {
targets.add(tmpList.get(i));
final ChangeInfo info = changes.get(tmpList.get(i));
info.mParent = i < tmpList.size() - 1 ? tmpList.get(i + 1) : topParent;
}
continue;
}
// No ancestors in participant-list, so wc is a top target.
targets.add(wc);
topTargets.add(wc);
targets.mValidParticipants.add(wc);
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Initial targets: %s",
targets.mArray);
// Combine the targets from bottom to top if possible.
tryPromote(targets, changes);
// Establish the relationship between the targets and their top changes.
populateParentChanges(targets, changes);
// Populate children lists
for (int i = targets.size() - 1; i >= 0; --i) {
if (changes.get(targets.valueAt(i)).mParent != null) {
changes.get(changes.get(targets.valueAt(i)).mParent).addChild(targets.valueAt(i));
}
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Initial targets: %s", targets);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Top targets: %s", topTargets);
// Combine targets by repeatedly going through the topTargets to see if they can be
// promoted until there aren't any promotions possible.
while (tryPromote(topTargets, targets, changes)) {
// Empty on purpose
}
return targets;
final ArrayList<WindowContainer> targetList = targets.getListSortedByZ();
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Final targets: %s", targetList);
return targetList;
}
/** Add any of `members` within `root` to `out` in top-to-bottom z-order. */
private static void addMembersInOrder(WindowContainer root, ArraySet<WindowContainer> members,
ArrayList<WindowContainer> out) {
for (int i = root.getChildCount() - 1; i >= 0; --i) {
final WindowContainer child = root.getChildAt(i);
addMembersInOrder(child, members, out);
if (members.contains(child)) {
out.add(child);
/** Populates parent to the change info and collects intermediate targets. */
private static void populateParentChanges(Targets targets,
ArrayMap<WindowContainer, ChangeInfo> changes) {
final ArrayList<WindowContainer<?>> intermediates = new ArrayList<>();
for (int i = targets.mValidParticipants.size() - 1; i >= 0; --i) {
WindowContainer<?> wc = targets.mValidParticipants.get(i);
// Go up if the participant has been represented by its parent.
while (targets.mArray.indexOfValue(wc) < 0 && wc.getParent() != null) {
wc = wc.getParent();
}
// Wallpaper must belong to the top (regardless of how nested it is in DisplayAreas).
final boolean skipIntermediateReports = isWallpaper(wc);
intermediates.clear();
// Collect the intermediate parents between target and top changed parent.
for (WindowContainer<?> p = wc.getParent(); p != null; p = p.getParent()) {
final ChangeInfo parentChange = changes.get(p);
if (parentChange == null || !parentChange.hasChanged(p)) break;
if (parentChange.mParent != null && !skipIntermediateReports) {
changes.get(wc).mParent = p;
// The chain above the parent was processed.
break;
}
if (targets.mValidParticipants.contains(p)) {
if (skipIntermediateReports) {
changes.get(wc).mParent = p;
} else {
intermediates.add(p);
}
// The parent reaches a participant.
break;
} else if (reportIfNotTop(p) && !skipIntermediateReports) {
intermediates.add(p);
}
}
if (intermediates.isEmpty()) continue;
// Add any always-report parents along the way.
changes.get(wc).mParent = intermediates.get(0);
for (int j = 0; j < intermediates.size() - 1; j++) {
final WindowContainer<?> intermediate = intermediates.get(j);
changes.get(intermediate).mParent = intermediates.get(j + 1);
targets.add(intermediate);
}
}
}
@@ -1201,32 +1140,36 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
/**
* Construct a TransitionInfo object from a set of targets and changes. Also populates the
* root surface.
* @param sortedTargets The targets sorted by z-order from top (index 0) to bottom.
*/
@VisibleForTesting
@NonNull
static TransitionInfo calculateTransitionInfo(@TransitionType int type, int flags,
ArraySet<WindowContainer> targets, ArrayMap<WindowContainer, ChangeInfo> changes) {
ArrayList<WindowContainer> sortedTargets,
ArrayMap<WindowContainer, ChangeInfo> changes) {
final TransitionInfo out = new TransitionInfo(type, flags);
final ArraySet<WindowContainer> appTargets = new ArraySet<>();
final ArraySet<WindowContainer> wallpapers = new ArraySet<>();
for (int i = targets.size() - 1; i >= 0; --i) {
(isWallpaper(targets.valueAt(i)) ? wallpapers : appTargets).add(targets.valueAt(i));
WindowContainer<?> topApp = null;
for (int i = 0; i < sortedTargets.size(); i++) {
final WindowContainer<?> wc = sortedTargets.get(i);
if (!isWallpaper(wc)) {
topApp = wc;
break;
}
}
// Find the top-most shared ancestor of app targets
if (appTargets.isEmpty()) {
if (topApp == null) {
out.setRootLeash(new SurfaceControl(), 0, 0);
return out;
}
WindowContainer ancestor = appTargets.valueAt(appTargets.size() - 1).getParent();
// Find the top-most shared ancestor of app targets.
WindowContainer<?> ancestor = topApp.getParent();
// Go up ancestor parent chain until all targets are descendants.
ancestorLoop:
while (ancestor != null) {
for (int i = appTargets.size() - 1; i >= 0; --i) {
final WindowContainer wc = appTargets.valueAt(i);
if (!wc.isDescendantOf(ancestor)) {
for (int i = sortedTargets.size() - 1; i >= 0; --i) {
final WindowContainer wc = sortedTargets.get(i);
if (!isWallpaper(wc) && !wc.isDescendantOf(ancestor)) {
ancestor = ancestor.getParent();
continue ancestorLoop;
}
@@ -1234,11 +1177,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
break;
}
// Sort targets top-to-bottom in Z. Check ALL targets here in case the display area itself
// is animating: then we want to include wallpapers at the right position.
ArrayList<WindowContainer> sortedTargets = new ArrayList<>();
addMembersInOrder(ancestor, targets, sortedTargets);
// make leash based on highest (z-order) direct child of ancestor with a participant.
WindowContainer leashReference = sortedTargets.get(0);
while (leashReference.getParent() != ancestor) {
@@ -1252,14 +1190,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
t.close();
out.setRootLeash(rootLeash, ancestor.getBounds().left, ancestor.getBounds().top);
// add the wallpapers at the bottom
for (int i = wallpapers.size() - 1; i >= 0; --i) {
final WindowContainer wc = wallpapers.valueAt(i);
// If the displayarea itself is animating, then the wallpaper was already added.
if (wc.isDescendantOf(ancestor)) break;
sortedTargets.add(wc);
}
// Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order.
final int count = sortedTargets.size();
for (int i = 0; i < count; ++i) {
@@ -1403,7 +1333,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
static class ChangeInfo {
// Usually "post" change state.
WindowContainer mParent;
ArraySet<WindowContainer> mChildren;
// State tracking
boolean mExistenceChanged = false;
@@ -1498,19 +1427,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
return flags;
}
void addChild(@NonNull WindowContainer wc) {
if (mChildren == null) {
mChildren = new ArraySet<>();
}
mChildren.add(wc);
}
void addChildren(@NonNull ArraySet<WindowContainer> wcs) {
if (mChildren == null) {
mChildren = new ArraySet<>();
}
mChildren.addAll(wcs);
}
}
/**
@@ -1603,4 +1519,64 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return b.toString();
}
}
/**
* The container to represent the depth relation for calculating transition targets. The window
* container with larger depth is put at larger index. For the same depth, higher z-order has
* larger index.
*/
private static class Targets {
/** All targets. Its keys (depth) are sorted in ascending order naturally. */
final SparseArray<WindowContainer<?>> mArray = new SparseArray<>();
/** The initial participants which have changes. */
final ArrayList<WindowContainer<?>> mValidParticipants = new ArrayList<>();
/** The targets which were represented by their parent. */
private ArrayList<WindowContainer<?>> mRemovedTargets;
private int mDepthFactor;
void add(WindowContainer<?> target) {
// The number of slots per depth is larger than the total number of window container,
// so the depth score (key) won't have collision.
if (mDepthFactor == 0) {
mDepthFactor = target.mWmService.mRoot.getTreeWeight() + 1;
}
int score = target.getPrefixOrderIndex();
WindowContainer<?> wc = target;
while (wc != null) {
final WindowContainer<?> parent = wc.getParent();
if (parent != null) {
score += mDepthFactor;
}
wc = parent;
}
mArray.put(score, target);
}
void remove(int index, WindowContainer<?> removingTarget) {
mArray.removeAt(index);
if (mRemovedTargets == null) {
mRemovedTargets = new ArrayList<>();
}
mRemovedTargets.add(removingTarget);
}
boolean wasParticipated(WindowContainer<?> wc) {
return mArray.indexOfValue(wc) >= 0
|| (mRemovedTargets != null && mRemovedTargets.contains(wc));
}
/** Returns the target list sorted by z-order in ascending order (index 0 is top). */
ArrayList<WindowContainer> getListSortedByZ() {
final SparseArray<WindowContainer<?>> arrayByZ = new SparseArray<>(mArray.size());
for (int i = mArray.size() - 1; i >= 0; --i) {
final int zOrder = mArray.keyAt(i) % mDepthFactor;
arrayByZ.put(zOrder, mArray.valueAt(i));
}
final ArrayList<WindowContainer> sortedTargets = new ArrayList<>(arrayByZ.size());
for (int i = arrayByZ.size() - 1; i >= 0; --i) {
sortedTargets.add(arrayByZ.valueAt(i));
}
return sortedTargets;
}
}
}

View File

@@ -661,6 +661,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
/** Returns the total number of descendants, including self. */
int getTreeWeight() {
return mTreeWeight;
}
/**
* @return The index of this element in the hierarchy tree in prefix order.
*/

View File

@@ -50,6 +50,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.SurfaceControl;
import android.view.TransactionCommittedListener;
import android.window.IDisplayAreaOrganizer;
import android.window.ITaskOrganizer;
import android.window.ITransitionPlayer;
import android.window.TransitionInfo;
@@ -60,6 +61,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -106,7 +109,7 @@ public class TransitionTests extends WindowTestsBase {
// Check basic both tasks participating
participants.add(oldTask);
participants.add(newTask);
ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes);
ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
assertEquals(2, info.getChanges().size());
assertEquals(transit, info.getType());
@@ -171,7 +174,7 @@ public class TransitionTests extends WindowTestsBase {
participants.add(oldTask);
participants.add(opening);
participants.add(opening2);
ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes);
ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
assertEquals(2, info.getChanges().size());
assertEquals(transit, info.getType());
@@ -217,15 +220,14 @@ public class TransitionTests extends WindowTestsBase {
// Check promotion to DisplayArea
participants.add(showing);
participants.add(showing2);
ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes);
ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
assertEquals(1, info.getChanges().size());
assertEquals(transit, info.getType());
assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken()));
ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
// Check that organized tasks get reported even if not top
showTask.mTaskOrganizer = mockOrg;
makeTaskOrganized(showTask);
targets = Transition.calculateTargets(participants, changes);
info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
assertEquals(2, info.getChanges().size());
@@ -255,7 +257,7 @@ public class TransitionTests extends WindowTestsBase {
opening.mVisibleRequested = true;
closing.mVisibleRequested = false;
ArraySet<WindowContainer> targets = Transition.calculateTargets(
ArrayList<WindowContainer> targets = Transition.calculateTargets(
transition.mParticipants, transition.mChanges);
TransitionInfo info = Transition.calculateTransitionInfo(
0, 0, targets, transition.mChanges);
@@ -292,7 +294,7 @@ public class TransitionTests extends WindowTestsBase {
tasks[i].getTopMostActivity().mVisibleRequested = (i % 2) != 0;
}
ArraySet<WindowContainer> targets = Transition.calculateTargets(
ArrayList<WindowContainer> targets = Transition.calculateTargets(
transition.mParticipants, transition.mChanges);
TransitionInfo info = Transition.calculateTransitionInfo(
0, 0, targets, transition.mChanges);
@@ -341,7 +343,7 @@ public class TransitionTests extends WindowTestsBase {
tasks[i].getTopMostActivity().mVisibleRequested = (i % 2) != 0;
}
ArraySet<WindowContainer> targets = Transition.calculateTargets(
ArrayList<WindowContainer> targets = Transition.calculateTargets(
transition.mParticipants, transition.mChanges);
TransitionInfo info = Transition.calculateTransitionInfo(
0, 0, targets, transition.mChanges);
@@ -363,13 +365,7 @@ public class TransitionTests extends WindowTestsBase {
mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
// Make DA organized so we can check that they don't get included.
WindowContainer parent = wallpaperWindowToken.getParent();
while (parent != null && parent != mDisplayContent) {
if (parent.asDisplayArea() != null) {
parent.asDisplayArea().setOrganizer(
mock(android.window.IDisplayAreaOrganizer.class), true /* skipAppear */);
}
parent = parent.getParent();
}
makeDisplayAreaOrganized(parent, mDisplayContent);
final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
"wallpaperWindow");
wallpaperWindowToken.setVisibleRequested(false);
@@ -381,7 +377,7 @@ public class TransitionTests extends WindowTestsBase {
mDisplayContent.getWindowConfiguration().setRotation(
(mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
ArraySet<WindowContainer> targets = Transition.calculateTargets(
ArrayList<WindowContainer> targets = Transition.calculateTargets(
transition.mParticipants, transition.mChanges);
TransitionInfo info = Transition.calculateTransitionInfo(
0, 0, targets, transition.mChanges);
@@ -393,12 +389,38 @@ public class TransitionTests extends WindowTestsBase {
info.getChanges().get(0).getParent());
}
@Test
public void testOpenActivityInTheSameTaskWithDisplayChange() {
final ActivityRecord closing = createActivityRecord(mDisplayContent);
closing.mVisibleRequested = true;
final Task task = closing.getTask();
makeTaskOrganized(task);
final ActivityRecord opening = createActivityRecord(task);
opening.mVisibleRequested = false;
makeDisplayAreaOrganized(mDisplayContent.getDefaultTaskDisplayArea(), mDisplayContent);
final WindowContainer<?>[] wcs = { closing, opening, task, mDisplayContent };
final Transition transition = createTestTransition(TRANSIT_OPEN);
for (WindowContainer<?> wc : wcs) {
transition.collect(wc);
}
closing.mVisibleRequested = false;
opening.mVisibleRequested = true;
final int newRotation = mDisplayContent.getWindowConfiguration().getRotation() + 1;
for (WindowContainer<?> wc : wcs) {
wc.getWindowConfiguration().setRotation(newRotation);
}
final ArrayList<WindowContainer> targets = Transition.calculateTargets(
transition.mParticipants, transition.mChanges);
// Especially the activities must be in the targets.
assertTrue(targets.containsAll(Arrays.asList(wcs)));
}
@Test
public void testIndependent() {
final Transition transition = createTestTransition(TRANSIT_OPEN);
ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
ArraySet<WindowContainer> participants = transition.mParticipants;
ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
final Task openTask = createTask(mDisplayContent);
final Task openInOpenTask = createTaskInRootTask(openTask, 0);
@@ -410,10 +432,8 @@ public class TransitionTests extends WindowTestsBase {
final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask);
final ActivityRecord openInChange = createActivityRecord(openInChangeTask);
// set organizer for everything so that they all get added to transition info
for (Task t : new Task[]{
openTask, openInOpenTask, changeTask, changeInChangeTask, openInChangeTask}) {
t.mTaskOrganizer = mockOrg;
}
makeTaskOrganized(openTask, openInOpenTask, changeTask, changeInChangeTask,
openInChangeTask);
// Start states.
changes.put(openTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
@@ -440,7 +460,8 @@ public class TransitionTests extends WindowTestsBase {
participants.add(openInChange);
// Explicitly add changeTask (to test independence with parents)
participants.add(changeTask);
ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes);
final ArrayList<WindowContainer> targets =
Transition.calculateTargets(participants, changes);
TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
// Root changes should always be considered independent
assertTrue(isIndependent(
@@ -581,17 +602,15 @@ public class TransitionTests extends WindowTestsBase {
final TransitionController controller = new TransitionController(mAtm, snapshotController);
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
// Start out with task2 visible and set up a transition that closes task2 and opens task1
final Task task1 = createTask(mDisplayContent);
task1.mTaskOrganizer = mockOrg;
final ActivityRecord activity1 = createActivityRecord(task1);
activity1.mVisibleRequested = false;
activity1.setVisible(false);
final Task task2 = createTask(mDisplayContent);
task2.mTaskOrganizer = mockOrg;
makeTaskOrganized(task1, task2);
final ActivityRecord activity2 = createActivityRecord(task1);
activity2.mVisibleRequested = true;
activity2.setVisible(true);
@@ -647,17 +666,15 @@ public class TransitionTests extends WindowTestsBase {
final TransitionController controller = new TransitionController(mAtm, snapshotController);
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
// Start out with task2 visible and set up a transition that closes task2 and opens task1
final Task task1 = createTask(mDisplayContent);
task1.mTaskOrganizer = mockOrg;
final ActivityRecord activity1 = createActivityRecord(task1);
activity1.mVisibleRequested = false;
activity1.setVisible(false);
final Task task2 = createTask(mDisplayContent);
task2.mTaskOrganizer = mockOrg;
makeTaskOrganized(task1, task2);
final ActivityRecord activity2 = createActivityRecord(task2);
activity2.mVisibleRequested = true;
activity2.setVisible(true);
@@ -706,6 +723,24 @@ public class TransitionTests extends WindowTestsBase {
verify(snapshotController, times(1)).recordTaskSnapshot(eq(task1), eq(false));
}
private static void makeTaskOrganized(Task... tasks) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
for (Task t : tasks) {
t.mTaskOrganizer = organizer;
}
}
private static void makeDisplayAreaOrganized(WindowContainer<?> from,
WindowContainer<?> end) {
final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class);
while (from != null && from != end) {
if (from.asDisplayArea() != null) {
from.asDisplayArea().mOrganizer = organizer;
}
from = from.getParent();
}
}
/** Fill the change map with all the parents of top. Change maps are usually fully populated */
private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes,
WindowContainer top) {