diff options
10 files changed, 264 insertions, 16 deletions
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 52ab38fa2889..da291cf0fd2c 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -57,6 +57,32 @@ public final class TransitionInfo implements Parcelable { }) public @interface TransitionMode {} + /** No flags */ + public static final int FLAG_NONE = 0; + + /** The container shows the wallpaper behind it. */ + public static final int FLAG_SHOW_WALLPAPER = 1; + + /** The container IS the wallpaper. */ + public static final int FLAG_IS_WALLPAPER = 1 << 1; + + /** The container is translucent. */ + public static final int FLAG_TRANSLUCENT = 1 << 2; + + // TODO: remove when starting-window is moved to Task + /** The container is the recipient of a transferred starting-window */ + public static final int FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT = 1 << 3; + + /** @hide */ + @IntDef(prefix = { "FLAG_" }, value = { + FLAG_NONE, + FLAG_SHOW_WALLPAPER, + FLAG_IS_WALLPAPER, + FLAG_TRANSLUCENT, + FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT + }) + public @interface ChangeFlags {} + private final @WindowManager.TransitionOldType int mType; private final @WindowManager.TransitionFlags int mFlags; private final ArrayList<Change> mChanges = new ArrayList<>(); @@ -198,12 +224,33 @@ public final class TransitionInfo implements Parcelable { } } + /** Converts change flags into a string representation. */ + @NonNull + public static String flagsToString(@ChangeFlags int flags) { + if (flags == 0) return "NONE"; + final StringBuilder sb = new StringBuilder(); + if ((flags & FLAG_SHOW_WALLPAPER) != 0) { + sb.append("SHOW_WALLPAPER"); + } + if ((flags & FLAG_IS_WALLPAPER) != 0) { + sb.append("IS_WALLPAPER"); + } + if ((flags & FLAG_TRANSLUCENT) != 0) { + sb.append((sb.length() == 0 ? "" : "|") + "TRANSLUCENT"); + } + if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + sb.append((sb.length() == 0 ? "" : "|") + "STARTING_WINDOW_TRANSFER"); + } + return sb.toString(); + } + /** Represents the change a WindowContainer undergoes during a transition */ public static final class Change implements Parcelable { private final WindowContainerToken mContainer; private WindowContainerToken mParent; private final SurfaceControl mLeash; private @TransitionMode int mMode = TRANSIT_NONE; + private @ChangeFlags int mFlags = FLAG_NONE; private final Rect mStartAbsBounds = new Rect(); private final Rect mEndAbsBounds = new Rect(); private final Point mEndRelOffset = new Point(); @@ -219,6 +266,7 @@ public final class TransitionInfo implements Parcelable { mLeash = new SurfaceControl(); mLeash.readFromParcel(in); mMode = in.readInt(); + mFlags = in.readInt(); mStartAbsBounds.readFromParcel(in); mEndAbsBounds.readFromParcel(in); mEndRelOffset.readFromParcel(in); @@ -234,6 +282,11 @@ public final class TransitionInfo implements Parcelable { mMode = mode; } + /** Sets the flags for this change */ + public void setFlags(@ChangeFlags int flags) { + mFlags = flags; + } + /** Sets the bounds this container occupied before the change in screen space */ public void setStartAbsBounds(@Nullable Rect rect) { mStartAbsBounds.set(rect); @@ -269,6 +322,11 @@ public final class TransitionInfo implements Parcelable { return mMode; } + /** @return the flags for this change. */ + public @ChangeFlags int getFlags() { + return mFlags; + } + /** * @return the bounds of the container before the change. It may be empty if the container * is coming into existence. @@ -308,6 +366,7 @@ public final class TransitionInfo implements Parcelable { dest.writeTypedObject(mParent, flags); mLeash.writeToParcel(dest, flags); dest.writeInt(mMode); + dest.writeInt(mFlags); mStartAbsBounds.writeToParcel(dest, flags); mEndAbsBounds.writeToParcel(dest, flags); mEndRelOffset.writeToParcel(dest, flags); @@ -336,8 +395,8 @@ public final class TransitionInfo implements Parcelable { @Override public String toString() { return "{" + mContainer + "(" + mParent + ") leash=" + mLeash - + " m=" + modeToString(mMode) + " sb=" + mStartAbsBounds - + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + "}"; + + " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb=" + + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + "}"; } } } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index faf49733050a..3f66efa505c8 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -73,6 +73,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-2036671725": { + "message": " SKIP: is wallpaper", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/Transition.java" + }, "-2029985709": { "message": "setFocusedTask: taskId=%d", "level": "DEBUG", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java index a779531f8b91..54863d23643a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java @@ -20,6 +20,7 @@ import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import android.animation.Animator; import android.animation.ValueAnimator; @@ -166,8 +167,14 @@ public class Transitions { if (isOpening) { // put on top and fade in t.setLayer(leash, info.getChanges().size() - i); - t.setAlpha(leash, 0.f); - startExampleAnimation(transitionToken, leash, true /* show */); + if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + // This received a transferred starting window, so make it immediately + // visible. + t.setAlpha(leash, 1.f); + } else { + t.setAlpha(leash, 0.f); + startExampleAnimation(transitionToken, leash, true /* show */); + } } else { // put on bottom and leave it visible without fade t.setLayer(leash, -i); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index ef845c843c5f..a930730ee09c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -569,7 +569,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults = new WindowState.UpdateReportedVisibilityResults(); - private boolean mUseTransferredAnimation; + boolean mUseTransferredAnimation; /** * @see #currentLaunchCanTurnScreenOn() @@ -3492,8 +3492,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Removing starting %s from %s", tStartingWindow, fromActivity); - fromActivity.removeChild(tStartingWindow); - addWindow(tStartingWindow); + mAtmService.getTransitionController().collect(tStartingWindow); + tStartingWindow.reparent(this, POSITION_TOP); // Propagate other interesting state between the tokens. If the old token is displayed, // we should immediately force the new one to be displayed. If it is animating, we need @@ -3518,6 +3518,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // the token we transfer the animation over. Thus, set this flag to indicate // we've transferred the animation. mUseTransferredAnimation = true; + } else if (mAtmService.getTransitionController().getTransitionPlayer() != null) { + // In the new transit system, just set this every time we transfer the window + mUseTransferredAnimation = true; } // Post cleanup after the visibility and animation are transferred. fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 14504d896052..be78d7a18377 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -30,6 +30,10 @@ 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; +import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; +import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; +import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; +import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import android.annotation.IntDef; import android.annotation.NonNull; @@ -160,8 +164,20 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } if (mParticipants.contains(wc)) return; mSyncEngine.addToSyncSet(mSyncId, wc); - mChanges.put(wc, new ChangeInfo(wc)); + ChangeInfo info = mChanges.get(wc); + if (info == null) { + info = new ChangeInfo(wc); + mChanges.put(wc, info); + } mParticipants.add(wc); + if (info.mShowWallpaper) { + // Collect the wallpaper so it is part of the sync set. + final WindowContainer wallpaper = + wc.getDisplayContent().mWallpaperController.getTopVisibleWallpaper(); + if (wallpaper != null) { + collect(wallpaper); + } + } } /** @@ -386,6 +402,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe return -1; } + private static boolean isWallpaper(WindowContainer wc) { + return wc instanceof WallpaperWindowToken; + } + /** * Under some conditions (eg. all visible targets within a parent container are transitioning * the same way) the transition can be "promoted" to the parent container. This means an @@ -403,6 +423,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe parent == null ? "no 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. @@ -604,18 +628,32 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe static TransitionInfo calculateTransitionInfo(int type, int flags, ArraySet<WindowContainer> targets, ArrayMap<WindowContainer, ChangeInfo> changes) { final TransitionInfo out = new TransitionInfo(type, flags); - if (targets.isEmpty()) { + + 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)); + } + + // Find the top-most shared ancestor of app targets + WindowContainer ancestor = null; + for (int i = appTargets.size() - 1; i >= 0; --i) { + final WindowContainer wc = appTargets.valueAt(i); + ancestor = wc; + break; + } + if (ancestor == null) { out.setRootLeash(new SurfaceControl(), 0, 0); return out; } + ancestor = ancestor.getParent(); - // Find the top-most shared ancestor - WindowContainer ancestor = targets.valueAt(0).getParent(); - // Go up ancestor parent chain until all topTargets are descendants. + // Go up ancestor parent chain until all targets are descendants. ancestorLoop: while (ancestor != null) { - for (int i = 1; i < targets.size(); ++i) { - if (!targets.valueAt(i).isDescendantOf(ancestor)) { + for (int i = appTargets.size() - 1; i >= 0; --i) { + final WindowContainer wc = appTargets.valueAt(i); + if (!wc.isDescendantOf(ancestor)) { ancestor = ancestor.getParent(); continue ancestorLoop; } @@ -623,7 +661,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe break; } - // Sort targets top-to-bottom in Z. + // 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); @@ -640,6 +679,14 @@ 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) { @@ -656,6 +703,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe change.setEndAbsBounds(target.getBounds()); change.setEndRelOffset(target.getBounds().left - target.getParent().getBounds().left, target.getBounds().top - target.getParent().getBounds().top); + change.setFlags(info.getChangeFlags(target)); out.addChange(change); } @@ -678,17 +726,20 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe boolean mVisible; int mWindowingMode; final Rect mAbsoluteBounds = new Rect(); + boolean mShowWallpaper; ChangeInfo(@NonNull WindowContainer origState) { mVisible = origState.isVisibleRequested(); mWindowingMode = origState.getWindowingMode(); mAbsoluteBounds.set(origState.getBounds()); + mShowWallpaper = origState.showWallpaper(); } @VisibleForTesting ChangeInfo(boolean visible, boolean existChange) { mVisible = visible; mExistenceChanged = existChange; + mShowWallpaper = false; } boolean hasChanged(@NonNull WindowContainer newState) { @@ -716,6 +767,29 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } } + @TransitionInfo.ChangeFlags + int getChangeFlags(@NonNull WindowContainer wc) { + int flags = 0; + if (mShowWallpaper || wc.showWallpaper()) { + flags |= FLAG_SHOW_WALLPAPER; + } + if (!wc.fillsParent()) { + // TODO(b/172695805): hierarchical check. This is non-trivial because for containers + // it is effected by child visibility but needs to work even + // before visibility is committed. This means refactoring some + // checks to use requested visibility. + flags |= FLAG_TRANSLUCENT; + } + if (wc instanceof ActivityRecord + && wc.asActivityRecord().mUseTransferredAnimation) { + flags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; + } + if (isWallpaper(wc)) { + flags |= FLAG_IS_WALLPAPER; + } + return flags; + } + void addChild(@NonNull WindowContainer wc) { if (mChildren == null) { mChildren = new ArraySet<>(); diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 7809cbc2a167..1a3138d492c8 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -779,7 +779,7 @@ class WallpaperController { wallpaperBuffer.getHardwareBuffer(), wallpaperBuffer.getColorSpace()); } - private WindowState getTopVisibleWallpaper() { + WindowState getTopVisibleWallpaper() { mTmpTopWallpaper = null; for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index c27d0cda22ab..f572e8e2f0aa 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -96,6 +96,7 @@ class WallpaperWindowToken extends WindowToken { void updateWallpaperVisibility(boolean visible) { if (isVisible() != visible) { + mWmService.mAtmService.getTransitionController().collect(this); // Need to do a layout to ensure the wallpaper now has the correct size. mDisplayContent.setLayoutNeeded(); } @@ -126,6 +127,7 @@ class WallpaperWindowToken extends WindowToken { if (isVisible() != visible) { if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG, "Wallpaper token " + token + " visible=" + visible); + mWmService.mAtmService.getTransitionController().collect(this); // Need to do a layout to ensure the wallpaper now has the correct size. mDisplayContent.setLayoutNeeded(); } @@ -200,6 +202,17 @@ class WallpaperWindowToken extends WindowToken { } @Override + boolean fillsParent() { + return true; + } + + @Override + boolean showWallpaper() { + return false; + } + + + @Override public String toString() { if (stringName == null) { StringBuilder sb = new StringBuilder(); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 576d22487437..19a750abe1a7 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -3077,6 +3077,23 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return true; } + /** @return {@code true} if the wallpaper is visible behind this container. */ + boolean showWallpaper() { + if (!isVisibleRequested() + // in multi-window mode, wallpaper is always visible at the back and not tied to + // the app (there is no wallpaper target). + || inMultiWindowMode()) { + return false; + } + for (int i = mChildren.size() - 1; i >= 0; --i) { + final WindowContainer child = mChildren.get(i); + if (child.showWallpaper()) { + return true; + } + } + return false; + } + @Nullable static WindowContainer fromBinder(IBinder binder) { return RemoteToken.fromBinder(binder).getContainer(); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a8f4bae1d519..11ed0a2b27e5 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5736,6 +5736,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mAttrs.type == TYPE_APPLICATION_STARTING; } + @Override + boolean showWallpaper() { + if (!isVisibleRequested() + // in multi-window mode, wallpaper is always visible at the back and not tied to + // the app (there is no wallpaper target). + || inMultiWindowMode()) { + return false; + } + return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0; + } + /** * When using the two WindowOrganizer sync-primitives (BoundsChangeTransaction, BLASTSync) * it can be a little difficult to predict whether your change will actually trigger redrawing 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 00c2aa5ebc63..74248a950a38 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -19,14 +19,21 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; +import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import android.util.ArraySet; @@ -280,4 +287,55 @@ public class TransitionTests extends WindowTestsBase { info.getChanges().get(i).getContainer()); } } + + @Test + public void testCreateInfo_wallpaper() { + final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN); + // pick some number with a high enough chance of being out-of-order when added to set. + final int taskCount = 4; + final int showWallpaperTask = 2; + + final Task[] tasks = new Task[taskCount]; + for (int i = 0; i < taskCount; ++i) { + // Each add goes on top, so at the end of this, task[9] should be on top + tasks[i] = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, mDisplayContent); + final ActivityRecord act = createActivityRecord(tasks[i]); + // alternate so that the transition doesn't get promoted to the display area + act.mVisibleRequested = (i % 2) == 0; // starts invisible + if (i == showWallpaperTask) { + doReturn(true).when(act).showWallpaper(); + } + } + + final WallpaperWindowToken wallpaperWindowToken = spy(new WallpaperWindowToken(mWm, + mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */)); + final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, + "wallpaperWindow"); + wallpaperWindow.mWallpaperVisible = false; + transition.collect(wallpaperWindowToken); + wallpaperWindow.mWallpaperVisible = true; + wallpaperWindow.mHasSurface = true; + + // doesn't matter which order collected since participants is a set + for (int i = 0; i < taskCount; ++i) { + transition.collectExistenceChange(tasks[i]); + final ActivityRecord act = tasks[i].getTopMostActivity(); + transition.collect(act); + tasks[i].getTopMostActivity().mVisibleRequested = (i % 2) != 0; + } + + ArraySet<WindowContainer> targets = Transition.calculateTargets( + transition.mParticipants, transition.mChanges); + TransitionInfo info = Transition.calculateTransitionInfo( + 0, 0, targets, transition.mChanges); + // verify that wallpaper is at bottom + assertEquals(taskCount + 1, info.getChanges().size()); + // The wallpaper is not organized, so it won't have a token; however, it will be marked + // as IS_WALLPAPER + assertEquals(FLAG_IS_WALLPAPER, + info.getChanges().get(info.getChanges().size() - 1).getFlags()); + assertEquals(FLAG_SHOW_WALLPAPER, info.getChange( + tasks[showWallpaperTask].mRemoteToken.toWindowContainerToken()).getFlags()); + } } |