summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Evan Rosky <erosky@google.com> 2023-03-02 01:01:24 -0800
committer Evan Rosky <erosky@google.com> 2023-03-06 20:25:24 +0000
commit032e5d004c629c900ef961b4e857b6a825a8cd5f (patch)
tree3ddb50b1fa0a5dcba1a7033df0dc754b3b986a8a
parent66948922135fb7f94f1fd1afa956e4d06f01ce4f (diff)
Use a SLEEP transition to trigger failsafe animation cancelling
Some remote animations can misbehave. In particular, recents. When this happens in the form of the remote not reporting finish, the transition system can get stuck waiting. Legacy recents had the same problem and solved it by cancelling the recents and independently finishing/cleaning-up after a "failsafe" duration from turning the screen off. This CL sets-up something similar for shell-transitions. It uses SLEEP as a signal to "quickly end all animations". This signal gets sent (via merge) to all the animations. Each animation gets a small amount of time to finish after-which we forcibly finish it. Bug: 267738124 Test: Observe sleep transitions and cancelling in logs. Change-Id: I36a856e1a798526ad0bb5006477dd08c5bb8792f
-rw-r--r--core/java/android/view/WindowManager.java9
-rw-r--r--data/etc/services.core.protolog.json12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java65
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java116
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java8
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java26
-rw-r--r--services/core/java/com/android/server/wm/Transition.java33
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java2
10 files changed, 270 insertions, 27 deletions
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index f8636788ee3c..dfb11bcee5ed 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -454,6 +454,11 @@ public interface WindowManager extends ViewManager {
*/
int TRANSIT_WAKE = 11;
/**
+ * The screen is turning off. This is used as a message to stop all animations.
+ * @hide
+ */
+ int TRANSIT_SLEEP = 12;
+ /**
* The first slot for custom transition types. Callers (like Shell) can make use of custom
* transition types for dealing with special cases. These types are effectively ignored by
* Core and will just be passed along as part of TransitionInfo objects. An example is
@@ -462,7 +467,7 @@ public interface WindowManager extends ViewManager {
* implementation.
* @hide
*/
- int TRANSIT_FIRST_CUSTOM = 12;
+ int TRANSIT_FIRST_CUSTOM = 13;
/**
* @hide
@@ -480,6 +485,7 @@ public interface WindowManager extends ViewManager {
TRANSIT_KEYGUARD_UNOCCLUDE,
TRANSIT_PIP,
TRANSIT_WAKE,
+ TRANSIT_SLEEP,
TRANSIT_FIRST_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
@@ -1437,6 +1443,7 @@ public interface WindowManager extends ViewManager {
case TRANSIT_KEYGUARD_UNOCCLUDE: return "KEYGUARD_UNOCCLUDE";
case TRANSIT_PIP: return "PIP";
case TRANSIT_WAKE: return "WAKE";
+ case TRANSIT_SLEEP: return "SLEEP";
case TRANSIT_FIRST_CUSTOM: return "FIRST_CUSTOM";
default:
if (type > TRANSIT_FIRST_CUSTOM) {
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 05e17720b175..913eaf2391f6 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1297,6 +1297,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-894942237": {
+ "message": "Force Playing Transition: %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"-883738232": {
"message": "Adding more than one toast window for UID at a time.",
"level": "WARN",
@@ -4243,12 +4249,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
- "1878927091": {
- "message": "prepareSurface: No changes in animation for %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ANIM",
- "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
- },
"1891501279": {
"message": "cancelAnimation(): reason=%s",
"level": "DEBUG",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index d1565d19aff0..75112b62c1c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -450,7 +450,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
// Already done, so no need to end it.
return;
}
- if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
+ if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
+ // queue since no actual animation.
+ } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
if (mixed.mAnimType == MixedTransition.ANIM_TYPE_GOING_HOME) {
boolean ended = mSplitHandler.end();
// If split couldn't end (because it is remote), then don't end everything else
@@ -464,8 +466,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
} else {
mPipHandler.end();
}
- } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
- // queue
+ } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
+ mPipHandler.end();
+ if (mixed.mLeftoversHandler != null) {
+ mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
+ finishCallback);
+ }
} else {
throw new IllegalStateException("Playing a mixed transition with unknown type? "
+ mixed.mType);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
new file mode 100644
index 000000000000..0386ec38a3ff
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 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.wm.shell.transition;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import java.util.ArrayList;
+
+/**
+ * A Simple handler that tracks SLEEP transitions. We track them specially since we (ab)use these
+ * as sentinels for fast-forwarding through animations when the screen is off.
+ *
+ * There should only be one SleepHandler and it is used explicitly by {@link Transitions} so we
+ * don't register it like a normal handler.
+ */
+class SleepHandler implements Transitions.TransitionHandler {
+ final ArrayList<IBinder> mSleepTransitions = new ArrayList<>();
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ startTransaction.apply();
+ finishCallback.onTransitionFinished(null, null);
+ mSleepTransitions.remove(transition);
+ return true;
+ }
+
+ @Override
+ @Nullable
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ mSleepTransitions.add(transition);
+ return new WindowContainerTransaction();
+ }
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishTransaction) {
+ Log.e(Transitions.TAG, "Sleep transition was consumed. This doesn't make sense");
+ mSleepTransitions.remove(transition);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index b39b95332e7f..155990a40836 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -21,6 +21,7 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
@@ -124,6 +125,7 @@ public class Transitions implements RemoteCallable<Transitions> {
private final DisplayController mDisplayController;
private final ShellController mShellController;
private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
+ private final SleepHandler mSleepHandler = new SleepHandler();
private boolean mIsRegistered = false;
@@ -137,6 +139,14 @@ public class Transitions implements RemoteCallable<Transitions> {
private float mTransitionAnimationScaleSetting = 1.0f;
+ /**
+ * How much time we allow for an animation to finish itself on sleep. If it takes longer, we
+ * will force-finish it (on this end) which may leave it in a bad state but won't hang the
+ * device. This needs to be pretty small because it is an allowance for each queued animation,
+ * however it can't be too small since there is some potential IPC involved.
+ */
+ private static final int SLEEP_ALLOWANCE_MS = 120;
+
private static final class ActiveTransition {
IBinder mToken;
TransitionHandler mHandler;
@@ -478,11 +488,29 @@ public class Transitions implements RemoteCallable<Transitions> {
+ Arrays.toString(mActiveTransitions.stream().map(
activeTransition -> activeTransition.mToken).toArray()));
}
+ final ActiveTransition active = mActiveTransitions.get(activeIdx);
for (int i = 0; i < mObservers.size(); ++i) {
mObservers.get(i).onTransitionReady(transitionToken, info, t, finishT);
}
+ if (info.getType() == TRANSIT_SLEEP) {
+ if (activeIdx > 0) {
+ active.mInfo = info;
+ active.mStartT = t;
+ active.mFinishT = finishT;
+ if (!info.getRootLeash().isValid()) {
+ // Shell has some debug settings which makes calling binders with invalid
+ // surfaces crash, so replace it with a "real" one.
+ info.setRootLeash(new SurfaceControl.Builder().setName("Invalid")
+ .setContainerLayer().build(), 0, 0);
+ }
+ // Sleep starts a process of forcing all prior transitions to finish immediately
+ finishForSleep(null /* forceFinish */);
+ return;
+ }
+ }
+
// Allow to notify keyguard un-occluding state to KeyguardService, which can happen while
// screen-off, so there might no visibility change involved.
if (!info.getRootLeash().isValid() && info.getType() != TRANSIT_KEYGUARD_UNOCCLUDE) {
@@ -527,7 +555,6 @@ public class Transitions implements RemoteCallable<Transitions> {
return;
}
- final ActiveTransition active = mActiveTransitions.get(activeIdx);
active.mInfo = info;
active.mStartT = t;
active.mFinishT = finishT;
@@ -803,23 +830,30 @@ public class Transitions implements RemoteCallable<Transitions> {
}
final ActiveTransition active = new ActiveTransition();
WindowContainerTransaction wct = null;
- for (int i = mHandlers.size() - 1; i >= 0; --i) {
- wct = mHandlers.get(i).handleRequest(transitionToken, request);
- if (wct != null) {
- active.mHandler = mHandlers.get(i);
- break;
+
+ // If we have sleep, we use a special handler and we try to finish everything ASAP.
+ if (request.getType() == TRANSIT_SLEEP) {
+ mSleepHandler.handleRequest(transitionToken, request);
+ active.mHandler = mSleepHandler;
+ } else {
+ for (int i = mHandlers.size() - 1; i >= 0; --i) {
+ wct = mHandlers.get(i).handleRequest(transitionToken, request);
+ if (wct != null) {
+ active.mHandler = mHandlers.get(i);
+ break;
+ }
}
- }
- if (request.getDisplayChange() != null) {
- TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
- if (change.getEndRotation() != change.getStartRotation()) {
- // Is a rotation, so dispatch to all displayChange listeners
- if (wct == null) {
- wct = new WindowContainerTransaction();
+ if (request.getDisplayChange() != null) {
+ TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
+ if (change.getEndRotation() != change.getStartRotation()) {
+ // Is a rotation, so dispatch to all displayChange listeners
+ if (wct == null) {
+ wct = new WindowContainerTransaction();
+ }
+ mDisplayController.getChangeController().dispatchOnDisplayChange(wct,
+ change.getDisplayId(), change.getStartRotation(),
+ change.getEndRotation(), null /* newDisplayAreaInfo */);
}
- mDisplayController.getChangeController().dispatchOnDisplayChange(wct,
- change.getDisplayId(), change.getStartRotation(), change.getEndRotation(),
- null /* newDisplayAreaInfo */);
}
}
mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct);
@@ -846,6 +880,56 @@ public class Transitions implements RemoteCallable<Transitions> {
}
/**
+ * Finish running animations (almost) immediately when a SLEEP transition comes in. We use this
+ * as both a way to reduce unnecessary work (animations not visible while screen off) and as a
+ * failsafe to unblock "stuck" animations (in particular remote animations).
+ *
+ * This works by "merging" the sleep transition into the currently-playing transition (even if
+ * its out-of-order) -- turning SLEEP into a signal. If the playing transition doesn't finish
+ * within `SLEEP_ALLOWANCE_MS` from this merge attempt, this will then finish it directly (and
+ * send an abort/consumed message).
+ *
+ * This is then repeated until there are no more pending sleep transitions.
+ *
+ * @param forceFinish When non-null, this is the transition that we last sent the SLEEP merge
+ * signal to -- so it will be force-finished if it's still running.
+ */
+ private void finishForSleep(@Nullable IBinder forceFinish) {
+ if (mActiveTransitions.isEmpty() || mSleepHandler.mSleepTransitions.isEmpty()) {
+ return;
+ }
+ if (forceFinish != null && mActiveTransitions.get(0).mToken == forceFinish) {
+ Log.e(TAG, "Forcing transition to finish due to sleep timeout: "
+ + mActiveTransitions.get(0).mToken);
+ onFinish(mActiveTransitions.get(0).mToken, null, null, true);
+ }
+ final SurfaceControl.Transaction dummyT = new SurfaceControl.Transaction();
+ while (!mActiveTransitions.isEmpty() && !mSleepHandler.mSleepTransitions.isEmpty()) {
+ final ActiveTransition playing = mActiveTransitions.get(0);
+ int sleepIdx = findActiveTransition(mSleepHandler.mSleepTransitions.get(0));
+ if (sleepIdx >= 0) {
+ // Try to signal that we are sleeping by attempting to merge the sleep transition
+ // into the playing one.
+ final ActiveTransition nextSleep = mActiveTransitions.get(sleepIdx);
+ playing.mHandler.mergeAnimation(nextSleep.mToken, nextSleep.mInfo, dummyT,
+ playing.mToken, (wct, cb) -> {});
+ } else {
+ Log.e(TAG, "Couldn't find sleep transition in active list: "
+ + mSleepHandler.mSleepTransitions.get(0));
+ }
+ // it's possible to complete immediately. If that happens, just repeat the signal
+ // loop until we either finish everything or start playing an animation that isn't
+ // finishing immediately.
+ if (!mActiveTransitions.isEmpty() && mActiveTransitions.get(0) == playing) {
+ // Give it a (very) short amount of time to process it before forcing.
+ mMainExecutor.executeDelayed(
+ () -> finishForSleep(playing.mToken), SLEEP_ALLOWANCE_MS);
+ break;
+ }
+ }
+ }
+
+ /**
* Interface for a callback that must be called after a TransitionHandler finishes playing an
* animation.
*/
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index fca55b1c69b4..6f7d66d03cab 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
+import static android.view.WindowManager.TRANSIT_SLEEP;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -46,6 +47,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
+import java.util.HashMap;
/**
* Helper class to build {@link RemoteTransition} objects
@@ -205,6 +207,12 @@ public class RemoteTransitionCompat {
@SuppressLint("NewApi")
boolean merge(TransitionInfo info, SurfaceControl.Transaction t) {
+ if (info.getType() == TRANSIT_SLEEP) {
+ // A sleep event means we need to stop animations immediately, so cancel here.
+ mListener.onAnimationCanceled(new HashMap<>());
+ finish(mWillFinishToHome, false /* userLeaveHint */);
+ return false;
+ }
ArrayList<TransitionInfo.Change> openingTasks = null;
ArrayList<TransitionInfo.Change> closingTasks = null;
mAppearedTargets = null;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b3b56f273f3d..e147219de4c6 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -33,6 +33,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_PIP;
+import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_WAKE;
@@ -2329,6 +2330,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
void applySleepTokens(boolean applyToRootTasks) {
+ boolean builtSleepTransition = false;
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
// Set the sleeping state of the display.
final DisplayContent display = getChildAt(displayNdx);
@@ -2338,6 +2340,30 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
display.setIsSleeping(displayShouldSleep);
+ if (display.mTransitionController.isShellTransitionsEnabled() && !builtSleepTransition
+ // Only care if there are actual sleep tokens.
+ && displayShouldSleep && !display.mAllSleepTokens.isEmpty()) {
+ builtSleepTransition = true;
+ // We don't actually care about collecting anything here. We really just want
+ // this as a signal to the transition-player.
+ final Transition transition = new Transition(TRANSIT_SLEEP, 0 /* flags */,
+ display.mTransitionController, mWmService.mSyncEngine);
+ final Runnable sendSleepTransition = () -> {
+ display.mTransitionController.requestStartTransition(transition,
+ null /* trigger */, null /* remote */, null /* display */);
+ // Force playing immediately so that unrelated ops can't be collected.
+ transition.playNow();
+ };
+ if (display.mTransitionController.isCollecting()) {
+ mWmService.mSyncEngine.queueSyncSet(
+ () -> display.mTransitionController.moveToCollecting(transition),
+ sendSleepTransition);
+ } else {
+ display.mTransitionController.moveToCollecting(transition);
+ sendSleepTransition.run();
+ }
+ }
+
if (!applyToRootTasks) {
continue;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a30ab11d9f6d..bf6983b90fbb 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -218,6 +218,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
final TransitionController.Logger mLogger = new TransitionController.Logger();
+ /** Whether this transition was forced to play early (eg for a SLEEP signal). */
+ private boolean mForcePlaying = false;
+
/**
* {@code false} if this transition runs purely in WMCore (meaning Shell is completely unaware
* of it). Currently, this happens before the display is ready since nothing can be seen yet.
@@ -389,6 +392,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return mState == STATE_COLLECTING || mState == STATE_STARTED;
}
+ boolean isAborted() {
+ return mState == STATE_ABORT;
+ }
+
boolean isStarted() {
return mState == STATE_STARTED;
}
@@ -997,6 +1004,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
void abort() {
// This calls back into itself via controller.abort, so just early return here.
if (mState == STATE_ABORT) return;
+ if (mState == STATE_PENDING) {
+ // hasn't started collecting, so can jump directly to aborted state.
+ mState = STATE_ABORT;
+ return;
+ }
if (mState != STATE_COLLECTING && mState != STATE_STARTED) {
throw new IllegalStateException("Too late to abort. state=" + mState);
}
@@ -1007,6 +1019,27 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mController.dispatchLegacyAppTransitionCancelled();
}
+ /** Immediately moves this to playing even if it isn't started yet. */
+ void playNow() {
+ if (!(mState == STATE_COLLECTING || mState == STATE_STARTED)) {
+ return;
+ }
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Force Playing Transition: %d",
+ mSyncId);
+ mForcePlaying = true;
+ setAllReady();
+ if (mState == STATE_COLLECTING) {
+ start();
+ }
+ // Don't wait for actual surface-placement. We don't want anything else collected in this
+ // transition.
+ mSyncEngine.onSurfacePlacement();
+ }
+
+ boolean isForcePlaying() {
+ return mForcePlaying;
+ }
+
void setRemoteTransition(RemoteTransition remoteTransition) {
mRemoteTransition = remoteTransition;
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 79eb63462d0b..6c951bfc5f9a 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -209,6 +209,12 @@ class TransitionController {
if (mCollectingTransition != null) {
throw new IllegalStateException("Simultaneous transition collection not supported.");
}
+ if (mTransitionPlayer == null) {
+ // If sysui has been killed (by a test) or crashed, we can temporarily have no player
+ // In this case, abort the transition.
+ transition.abort();
+ return;
+ }
mCollectingTransition = transition;
// Distinguish change type because the response time is usually expected to be not too long.
final long timeoutMs =
@@ -511,6 +517,14 @@ class TransitionController {
transition.getToken(), null));
return transition;
}
+ if (mTransitionPlayer == null || transition.isAborted()) {
+ // Apparently, some tests will kill(and restart) systemui, so there is a chance that
+ // the player might be transiently null.
+ if (transition.isCollecting()) {
+ transition.abort();
+ }
+ return transition;
+ }
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Requesting StartTransition: %s", transition);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index b9cb59a17a2e..495d7ce4e90b 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -317,7 +317,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
transition = mTransitionController.createTransition(type);
}
- if (!transition.isCollecting()) {
+ if (!transition.isCollecting() && !transition.isForcePlaying()) {
Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
+ " means Shell took too long to respond to a request. WM State may be"
+ " incorrect now, please file a bug");