diff options
author | Riddle Hsu <riddlehsu@google.com> | 2023-11-21 18:15:46 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-12-15 07:19:47 +0000 |
commit | 19f6b2f62c8865130b028a1a9258fd548a34e152 (patch) | |
tree | 44fd0c8e23556fdf473727940720dfba65e2ef06 | |
parent | 7f17bb5c5a6e33e3891ae3ba38a0aa21f889b550 (diff) | |
download | base-19f6b2f62c8865130b028a1a9258fd548a34e152.tar.gz |
Update surface visibility state when forcing visible
Since surface visibility recovery always applies for visible request
even for sleeping case, when finishing an activity while sleeping,
the next activity will enter onVisibleWithoutCollectingTransition
because there won't be a transition for invisible finishing activity.
And then because the device is sleeping, the next activity will be
stopped and invisible directly right after resuming. But its surface
visibility is only forced to be visible.
Then after the device is awake and the "invisible-visible" activity
doesn't have visibility change, home will be untouchable because
the surface layer of home task is always at bottom, which is under
the ActivityRecordInputSink of the problematic activity. The surface
hierarchy and visibility will be:
[Top]Task T (visible)
> Activity X (visible)
> Window (invisible)
> ActivityRecordInputSink (follow parent, so visible)
[Bottom]Task Home (visible)
> Activity (visible)
> Window (visible)
Even if X is stopped and mVisible/mVisibleRequested are false.
So this change updates mLastSurfaceShowing when forcing visible.
Then when executing ActivityRecord/Task#prepareSurface, it can
follow the current state of hierarchy to refresh the visibility.
Also narrow down the scope of surface visibility recovery while
display is sleeping, which is for show-when-locked case.
Fix: 311221846
Fix: 311379188
Fix: 309908645
Test: atest ActivityRecordTests# \
testFinishActivityIfPossible_nonVisibleNoAppTransition
Test: Launch app A. Launch app B. Turn off screen. B calls finish.
Launch app C (so A doesn't have visibility change later).
Unlock device. Press home key. Home can still scroll.
(App A,B,C have different uids so InputSink will block touch)
(cherry picked from commit 1c18c583b6b057ed72cd41b30a7a644f3047fa06)
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:450cb0f9e4f1efa98578028910d1494cf2317935)
Merged-In: I1d6c2e5a0dad831a9a7043d390037ca15a8b14bd
Change-Id: I1d6c2e5a0dad831a9a7043d390037ca15a8b14bd
4 files changed, 30 insertions, 8 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d570c9e1acab..add27078b00f 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -700,7 +700,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private boolean mCurrentLaunchCanTurnScreenOn = true; /** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */ - private boolean mLastSurfaceShowing; + boolean mLastSurfaceShowing; /** * The activity is opaque and fills the entire space of this task. @@ -5345,11 +5345,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Finish should only ever commit visibility=false, so we can check full containment // rather than just direct membership. inFinishingTransition = mTransitionController.inFinishingTransition(this); - if (!inFinishingTransition && (visible || !mDisplayContent.isSleeping())) { + if (!inFinishingTransition) { if (visible) { - mTransitionController.onVisibleWithoutCollectingTransition(this, - Debug.getCallers(1, 1)); - } else { + if (!mDisplayContent.isSleeping() || canShowWhenLocked()) { + mTransitionController.onVisibleWithoutCollectingTransition(this, + Debug.getCallers(1, 1)); + } + } else if (!mDisplayContent.isSleeping()) { Slog.w(TAG, "Set invisible without transition " + this); } } diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java index be7d9b63f779..15a3f9349007 100644 --- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java +++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java @@ -87,9 +87,11 @@ class ActivityRecordInputSink { activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid() || activityBelowInTask.isUid(mActivityRecord.getUid())); if (allowPassthrough || !mIsCompatEnabled || mActivityRecord.isInTransition()) { + // Set to non-touchable, so the touch events can pass through. mInputWindowHandleWrapper.setInputConfigMasked(InputConfig.NOT_TOUCHABLE, InputConfig.NOT_TOUCHABLE); } else { + // Set to touchable, so it can block by intercepting the touch events. mInputWindowHandleWrapper.setInputConfigMasked(0, InputConfig.NOT_TOUCHABLE); } mInputWindowHandleWrapper.setDisplayId(mActivityRecord.getDisplayId()); diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 577e755c310c..3ad42328a2df 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -971,11 +971,19 @@ class TransitionController { private void enforceSurfaceVisible(WindowContainer<?> wc) { if (wc.mSurfaceControl == null) return; wc.getSyncTransaction().show(wc.mSurfaceControl); + final ActivityRecord ar = wc.asActivityRecord(); + if (ar != null) { + ar.mLastSurfaceShowing = true; + } // Force showing the parents because they may be hidden by previous transition. for (WindowContainer<?> p = wc.getParent(); p != null && p != wc.mDisplayContent; p = p.getParent()) { if (p.mSurfaceControl != null) { p.getSyncTransaction().show(p.mSurfaceControl); + final Task task = p.asTask(); + if (task != null) { + task.mLastSurfaceShowing = true; + } } } wc.scheduleAnimation(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 3eed0b72e0bb..185f5ba9316e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1179,10 +1179,12 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testFinishActivityIfPossible_nonVisibleNoAppTransition() { registerTestTransitionPlayer(); + spyOn(mRootWindowContainer.mTransitionController); + final ActivityRecord bottomActivity = createActivityWithTask(); + bottomActivity.setVisibility(false); + bottomActivity.setState(STOPPED, "test"); + bottomActivity.mLastSurfaceShowing = false; final ActivityRecord activity = createActivityWithTask(); - // Put an activity on top of test activity to make it invisible and prevent us from - // accidentally resuming the topmost one again. - new ActivityBuilder(mAtm).build(); activity.setVisibleRequested(false); activity.setState(STOPPED, "test"); @@ -1190,6 +1192,14 @@ public class ActivityRecordTests extends WindowTestsBase { verify(activity.mDisplayContent, never()).prepareAppTransition(eq(TRANSIT_CLOSE)); assertFalse(activity.inTransition()); + + // finishIfPossible -> completeFinishing -> addToFinishingAndWaitForIdle + // -> resumeFocusedTasksTopActivities + assertTrue(bottomActivity.isState(RESUMED)); + assertTrue(bottomActivity.isVisible()); + verify(mRootWindowContainer.mTransitionController).onVisibleWithoutCollectingTransition( + eq(bottomActivity), any()); + assertTrue(bottomActivity.mLastSurfaceShowing); } /** |