summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Evan Rosky <erosky@google.com> 2023-02-15 14:03:25 -0800
committer Evan Rosky <erosky@google.com> 2023-02-17 21:17:03 +0000
commit66a2adc46af2b2a9d92fe0161ec083eb9a5d159c (patch)
treead187482e0dabc4cfea8fd6c0c8904daaa93e879
parent74efadf7387845552752eeade855b47bc283b6b3 (diff)
Add a fallback check to prevent half-way pip anims
This situation leaves the device in an unusable state, so add a hard-check for this to recover if it happens unexpectedly. This code should never run; however, just to be safe, its preferable to fallback and log an error instead of make the device unusable if an edge-case does get hit. Bug: 267897440 Test: purposefully disable some pip handling in shell and verify that the fallback occurs Change-Id: I619b49d5c4dedd92fac5ea3b8e133c8c50a72d9b
-rw-r--r--services/core/java/com/android/server/wm/Task.java30
-rw-r--r--services/core/java/com/android/server/wm/Transition.java19
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java23
3 files changed, 72 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 10e4929ded6a..2d54111028a6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4588,6 +4588,36 @@ class Task extends TaskFragment {
}
}
+ /**
+ * Abort an incomplete pip-entry. If left in this state, it will cover everything but remain
+ * paused. If this is needed, there is a bug -- this should only be used for recovery.
+ */
+ void abortPipEnter(ActivityRecord top) {
+ // an incomplete state has the task PINNED but the activity not.
+ if (!inPinnedWindowingMode() || top.inPinnedWindowingMode() || !canMoveTaskToBack(this)) {
+ return;
+ }
+ final Transition transition = new Transition(TRANSIT_TO_BACK, 0 /* flags */,
+ mTransitionController, mWmService.mSyncEngine);
+ mTransitionController.moveToCollecting(transition);
+ mTransitionController.requestStartTransition(transition, this, null /* remoteTransition */,
+ null /* displayChange */);
+ if (top.getLastParentBeforePip() != null) {
+ final Task lastParentBeforePip = top.getLastParentBeforePip();
+ if (lastParentBeforePip.isAttached()) {
+ top.reparent(lastParentBeforePip, lastParentBeforePip.getChildCount() /* top */,
+ "movePinnedActivityToOriginalTask");
+ }
+ }
+ if (isAttached()) {
+ setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ moveTaskToBackInner(this);
+ }
+ if (top.isAttached()) {
+ top.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ }
+ }
+
void resumeNextFocusAfterReparent() {
adjustFocusToNextFocusableTask("reparent", true /* allowFocusSelf */,
true /* moveDisplayToTop */);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 4e41da0e5d0b..a68b3cb2a2ea 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -878,6 +878,25 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
" Commit wallpaper becoming invisible: %s", wt);
wt.commitVisibility(false /* visible */);
}
+ continue;
+ }
+ final Task tr = participant.asTask();
+ if (tr != null && tr.isVisibleRequested() && tr.inPinnedWindowingMode()) {
+ final ActivityRecord top = tr.getTopNonFinishingActivity();
+ if (top != null && !top.inPinnedWindowingMode()) {
+ mController.mStateValidators.add(() -> {
+ if (!tr.isAttached() || !tr.isVisibleRequested()
+ || !tr.inPinnedWindowingMode()) return;
+ final ActivityRecord currTop = tr.getTopNonFinishingActivity();
+ if (currTop.inPinnedWindowingMode()) return;
+ Slog.e(TAG, "Enter-PIP was started but not completed, this is a Shell/SysUI"
+ + " bug. This state breaks gesture-nav, so attempting clean-up.");
+ // We don't know the destination bounds, so we can't actually finish the
+ // operation. So, to prevent the half-pipped task from covering everything,
+ // abort the action (which moves the task to back).
+ tr.abortPipEnter(currTop);
+ });
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index ed7e9ed78446..397abdb6e501 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -97,6 +97,12 @@ class TransitionController {
new ArrayList<>();
/**
+ * List of runnables to run when there are no ongoing transitions. Use this for state-validation
+ * checks (eg. to recover from incomplete states). Eventually this should be removed.
+ */
+ final ArrayList<Runnable> mStateValidators = new ArrayList<>();
+
+ /**
* Currently playing transitions (in the order they were started). When finished, records are
* removed from this list.
*/
@@ -659,6 +665,23 @@ class TransitionController {
updateRunningRemoteAnimation(record, false /* isPlaying */);
record.finishTransition();
mRunningLock.doNotifyLocked();
+ // Run state-validation checks when no transitions are active anymore.
+ if (!inTransition()) {
+ validateStates();
+ }
+ }
+
+ private void validateStates() {
+ for (int i = 0; i < mStateValidators.size(); ++i) {
+ mStateValidators.get(i).run();
+ if (inTransition()) {
+ // the validator may have started a new transition, so wait for that before
+ // checking the rest.
+ mStateValidators.subList(0, i + 1).clear();
+ return;
+ }
+ }
+ mStateValidators.clear();
}
void moveToPlaying(Transition transition) {