diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 8f73b9a12da2f..15672332a5220 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -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", diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 9b6827e3c66ac..ded58f48b207d 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -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 mParticipants = new ArraySet<>(); /** The final animation targets derived from participants after promotion. */ - private ArraySet mTargets = null; + private ArrayList 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 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 topTargets, + private static boolean canPromote(WindowContainer target, Targets targets, ArrayMap 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 topTargets, - ArraySet targets, ArrayMap 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 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 calculateTargets(ArraySet participants, + static ArrayList calculateTargets(ArraySet participants, ArrayMap changes) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start calculating TransitionInfo based on participants: %s", participants); - final ArraySet topTargets = new ArraySet<>(); - // The final animation targets which cannot promote to higher level anymore. - final ArraySet targets = new ArraySet<>(); - - final ArrayList 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 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 members, - ArrayList 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 changes) { + final ArrayList> 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 targets, ArrayMap changes) { + ArrayList sortedTargets, + ArrayMap changes) { final TransitionInfo out = new TransitionInfo(type, flags); - final ArraySet appTargets = new ArraySet<>(); - final ArraySet 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 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 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 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> mArray = new SparseArray<>(); + /** The initial participants which have changes. */ + final ArrayList> mValidParticipants = new ArrayList<>(); + /** The targets which were represented by their parent. */ + private ArrayList> 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 getListSortedByZ() { + final SparseArray> 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 sortedTargets = new ArrayList<>(arrayByZ.size()); + for (int i = arrayByZ.size() - 1; i >= 0; --i) { + sortedTargets.add(arrayByZ.valueAt(i)); + } + return sortedTargets; + } + } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 1205dee595b90..11d19839d4016 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -661,6 +661,11 @@ class 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. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 9bb4a4f3b8b27..fd523f0585c78 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -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 targets = Transition.calculateTargets(participants, changes); + ArrayList 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 targets = Transition.calculateTargets(participants, changes); + ArrayList 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 targets = Transition.calculateTargets(participants, changes); + ArrayList 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 targets = Transition.calculateTargets( + ArrayList 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 targets = Transition.calculateTargets( + ArrayList 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 targets = Transition.calculateTargets( + ArrayList 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 targets = Transition.calculateTargets( + ArrayList 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 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 changes = transition.mChanges; ArraySet 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 targets = Transition.calculateTargets(participants, changes); + final ArrayList 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 changes, WindowContainer top) {