Add non app windows to remote animation target.
Bug: 175686682
Test: atest RemoteAnimationControllerTest
Change-Id: I1a27af174536d3cebad5a7368d31f9f8290a0974
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ded4a27..fc15cff 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -259,6 +259,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1834214907": {
+ "message": "createNonAppWindowAnimations()",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
"-1824578273": {
"message": "Reporting new frame to %s: %s",
"level": "VERBOSE",
@@ -811,6 +817,18 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "-1153814764": {
+ "message": "onAnimationCancelled",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java"
+ },
+ "-1144293044": {
+ "message": "SURFACE SET FREEZE LAYER: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"-1142279614": {
"message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
"level": "VERBOSE",
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6a441f1..4ccd57d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -56,13 +56,11 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -2292,25 +2290,6 @@
return attrs.type == TYPE_NOTIFICATION_SHADE;
}
- @Override
- public boolean canBeHiddenByKeyguardLw(WindowState win) {
-
- // Keyguard visibility of window from activities are determined over activity visibility.
- if (win.getAppToken() != null) {
- return false;
- }
- switch (win.getAttrs().type) {
- case TYPE_NOTIFICATION_SHADE:
- case TYPE_STATUS_BAR:
- case TYPE_NAVIGATION_BAR:
- case TYPE_WALLPAPER:
- return false;
- default:
- // Hide only windows below the keyguard host window.
- return getWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE);
- }
- }
-
/** {@inheritDoc} */
@Override
public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index db33e75..b5a9aca 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -672,7 +672,22 @@
/**
* @return whether {@param win} can be hidden by Keyguard
*/
- public boolean canBeHiddenByKeyguardLw(WindowState win);
+ default boolean canBeHiddenByKeyguardLw(WindowState win) {
+ // Keyguard visibility of window from activities are determined over activity visibility.
+ if (win.getAppToken() != null) {
+ return false;
+ }
+ switch (win.getAttrs().type) {
+ case TYPE_NOTIFICATION_SHADE:
+ case TYPE_STATUS_BAR:
+ case TYPE_NAVIGATION_BAR:
+ case TYPE_WALLPAPER:
+ return false;
+ default:
+ // Hide only windows below the keyguard host window.
+ return getWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE);
+ }
+ }
/**
* Called when the system would like to show a UI to indicate that an
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
new file mode 100644
index 0000000..5d6d513
--- /dev/null
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
+import static com.android.server.wm.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
+
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.util.proto.ProtoOutputStream;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.policy.WindowManagerPolicy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+class NonAppWindowAnimationAdapter implements AnimationAdapter {
+
+ private final WindowState mWindow;
+ private RemoteAnimationTarget mTarget;
+ private SurfaceControl mCapturedLeash;
+
+ private long mDurationHint;
+ private long mStatusBarTransitionDelay;
+
+ @Override
+ public boolean getShowWallpaper() {
+ return false;
+ }
+
+ NonAppWindowAnimationAdapter(WindowState w,
+ long durationHint, long statusBarTransitionDelay) {
+ mWindow = w;
+ mDurationHint = durationHint;
+ mStatusBarTransitionDelay = statusBarTransitionDelay;
+ }
+
+ /**
+ * Creates and starts remote animations for all the visible non app windows.
+ *
+ * @return RemoteAnimationTarget[] targets for all the visible non app windows
+ */
+ public static RemoteAnimationTarget[] startNonAppWindowAnimationsForKeyguardExit(
+ WindowManagerService service, long durationHint, long statusBarTransitionDelay) {
+ final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
+
+ final WindowManagerPolicy policy = service.mPolicy;
+ service.mRoot.forAllWindows(nonAppWindow -> {
+ if (nonAppWindow.mActivityRecord == null && policy.canBeHiddenByKeyguardLw(nonAppWindow)
+ && nonAppWindow.wouldBeVisibleIfPolicyIgnored() && !nonAppWindow.isVisible()) {
+ final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter(
+ nonAppWindow, durationHint, statusBarTransitionDelay);
+ nonAppWindow.startAnimation(nonAppWindow.getPendingTransaction(),
+ nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION);
+ targets.add(nonAppAdapter.createRemoteAnimationTarget());
+ }
+ }, true /* traverseTopToBottom */);
+ return targets.toArray(new RemoteAnimationTarget[targets.size()]);
+ }
+
+ /**
+ * Create a remote animation target for this animation adapter.
+ */
+ RemoteAnimationTarget createRemoteAnimationTarget() {
+ mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false,
+ new Rect(), null, mWindow.getPrefixOrderIndex(), mWindow.getLastSurfacePosition(),
+ mWindow.getBounds(), null, mWindow.getWindowConfiguration(), true, null, null,
+ null);
+ return mTarget;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+ int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ mCapturedLeash = animationLeash;
+ }
+
+ @Override
+ public long getDurationHint() {
+ return mDurationHint;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return SystemClock.uptimeMillis() + mStatusBarTransitionDelay;
+ }
+
+ /**
+ * @return the leash for this animation (only valid after the non app window surface animation
+ * has started).
+ */
+ SurfaceControl getLeash() {
+ return mCapturedLeash;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationCancelled");
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print("token=");
+ pw.println(mWindow.mToken);
+ if (mTarget != null) {
+ pw.print(prefix);
+ pw.println("Target:");
+ mTarget.dump(pw, prefix + " ");
+ } else {
+ pw.print(prefix);
+ pw.println("Target: null");
+ }
+ }
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto) {
+ final long token = proto.start(REMOTE);
+ if (mTarget != null) {
+ mTarget.dumpDebug(proto, TARGET);
+ }
+ proto.end(token);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 392f27e..42cb96f 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
@@ -126,7 +129,7 @@
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
// TODO(bc-unlock): Create the remote non app animation targets (if any)
- final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
+ final RemoteAnimationTarget[] nonAppTargets = createNonAppWindowAnimations(transit);
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
@@ -215,6 +218,17 @@
}, mPendingWallpaperAnimations);
}
+ private RemoteAnimationTarget[] createNonAppWindowAnimations(
+ @WindowManager.TransitionOldType int transit) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createNonAppWindowAnimations()");
+ return (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+ || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER)
+ ? NonAppWindowAnimationAdapter.startNonAppWindowAnimationsForKeyguardExit(mService,
+ mRemoteAnimationAdapter.getDuration(),
+ mRemoteAnimationAdapter.getStatusBarTransitionDelay())
+ : new RemoteAnimationTarget[0];
+ }
+
private void onAnimationFinished() {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationFinished(): mPendingAnimations=%d",
mPendingAnimations.size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 15e045c..2fdd63e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -16,8 +16,11 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -94,10 +97,19 @@
mController = new RemoteAnimationController(mWm, mAdapter, mHandler);
}
+ private WindowState createAppOverlayWindow() {
+ final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION_OVERLAY,
+ "testOverlayWindow");
+ win.mActivityRecord = null;
+ win.mHasSurface = true;
+ return win;
+ }
+
@Test
public void testRun() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ final WindowState overlayWin = createAppOverlayWindow();
try {
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
@@ -109,12 +121,12 @@
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
- final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor =
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
- appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -130,6 +142,7 @@
finishedCaptor.getValue().onAnimationFinished();
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
eq(adapter));
+ assertEquals(0, nonAppsCaptor.getValue().length);
} finally {
mDisplayContent.mOpeningApps.clear();
}
@@ -424,6 +437,89 @@
}
}
+ @Test
+ public void testNonAppIncluded_keygaurdGoingAway() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ // Add overlay window hidden by the keyguard.
+ final WindowState overlayWin = createAppOverlayWindow();
+ overlayWin.hide(false /* doAnimation */, false /* requestAnim */);
+ try {
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(50, 100), null,
+ new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
+ mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_KEYGUARD_GOING_AWAY),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertEquals(new Point(50, 100), app.position);
+ assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds);
+ assertEquals(win.mActivityRecord.getPrefixOrderIndex(), app.prefixOrderIndex);
+ assertEquals(win.mActivityRecord.getTask().mTaskId, app.taskId);
+ assertEquals(mMockLeash, app.leash);
+ assertEquals(false, app.isTranslucent);
+ verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y);
+ verify(mMockTransaction).setWindowCrop(mMockLeash, 100, 50);
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
+ eq(adapter));
+ assertEquals(1, nonAppsCaptor.getValue().length);
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
+ @Test
+ public void testNonAppIncluded_keygaurdGoingAwayToWallpaper() throws Exception {
+ final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
+ true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ // Add overlay window hidden by the keyguard.
+ final WindowState overlayWin = createAppOverlayWindow();
+ overlayWin.hide(false /* doAnimation */, false /* requestAnim */);
+ try {
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(50, 100), null,
+ new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
+ mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, wallpapersCaptor.getValue().length);
+ assertEquals(1, nonAppsCaptor.getValue().length);
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index e4b865f..86d8eee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -91,11 +91,6 @@
return attrs.type == TYPE_NOTIFICATION_SHADE;
}
- @Override
- public boolean canBeHiddenByKeyguardLw(WindowState win) {
- return false;
- }
-
/**
* Sets a runnable to run when adding a splash screen which gets executed after the window has
* been added but before returning the surface.