diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 50ef91f909977..341c2147c64a1 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -26,7 +26,6 @@ import android.annotation.Nullable; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Rect; -import android.os.UidProto.Sync; import android.util.ArraySet; import android.util.SparseArray; import android.util.SparseIntArray; @@ -40,7 +39,6 @@ import android.view.WindowManager.LayoutParams; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; -import java.util.function.Function; import java.util.function.Supplier; /** @@ -238,7 +236,12 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame); state.getSource(source.getType()).setFrame(mTmpFrame); - surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f, inset != 0)); + + // If the system is controlling the insets source, the leash can be null. + if (leash != null) { + surfaceParams.add(new SurfaceParams(leash, 1f /* alpha */, mTmpMatrix, + null /* windowCrop */, 0 /* layer */, 0f /* cornerRadius*/, inset != 0)); + } } } diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 9edccb3fb221b..08d45a746dc41 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -167,7 +167,7 @@ public class InsetsSourceConsumer { } private void applyHiddenToControl() { - if (mSourceControl == null) { + if (mSourceControl == null || mSourceControl.getLeash() == null) { return; } diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index 4940981748a8b..4919074ec252e 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.Nullable; import android.graphics.Point; import android.os.Parcel; import android.os.Parcelable; @@ -28,10 +29,10 @@ import android.view.InsetsState.InternalInsetType; public class InsetsSourceControl implements Parcelable { private final @InternalInsetType int mType; - private final SurfaceControl mLeash; + private final @Nullable SurfaceControl mLeash; private final Point mSurfacePosition; - public InsetsSourceControl(@InternalInsetType int type, SurfaceControl leash, + public InsetsSourceControl(@InternalInsetType int type, @Nullable SurfaceControl leash, Point surfacePosition) { mType = type; mLeash = leash; @@ -42,7 +43,13 @@ public class InsetsSourceControl implements Parcelable { return mType; } - public SurfaceControl getLeash() { + /** + * Gets the leash for controlling insets source. If the system is controlling the insets source, + * for example, transient bars, the client will receive fake controls without leash in it. + * + * @return the leash. + */ + public @Nullable SurfaceControl getLeash() { return mLeash; } diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 1b7b92bca2507..34253ed6fc8cd 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -51,8 +51,11 @@ class InsetsSourceProvider { private final @NonNull InsetsSource mSource; private final DisplayContent mDisplayContent; private final InsetsStateController mStateController; + private final InsetsSourceControl mFakeControl; private @Nullable InsetsSourceControl mControl; private @Nullable InsetsControlTarget mControlTarget; + private @Nullable InsetsControlTarget mFakeControlTarget; + private @Nullable ControlAdapter mAdapter; private WindowState mWin; private TriConsumer mFrameProvider; @@ -73,6 +76,8 @@ class InsetsSourceProvider { mSource = source; mDisplayContent = displayContent; mStateController = stateController; + mFakeControl = new InsetsSourceControl(source.getType(), null /* leash */, + new Point()); final int type = source.getType(); if (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR) { @@ -150,6 +155,16 @@ class InsetsSourceProvider { && !mWin.mGivenInsetsPending); } + /** + * @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget) + */ + void updateControlForFakeTarget(@Nullable InsetsControlTarget fakeTarget) { + if (fakeTarget == mFakeControlTarget) { + return; + } + mFakeControlTarget = fakeTarget; + } + void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) { if (mWin == null) { mControlTarget = target; @@ -199,8 +214,14 @@ class InsetsSourceProvider { mSource.setVisible(mServerVisible && mClientVisible); } - InsetsSourceControl getControl() { - return mControl; + InsetsSourceControl getControl(InsetsControlTarget target) { + if (target == mControlTarget) { + return mControl; + } + if (target == mFakeControlTarget) { + return mFakeControl; + } + return null; } boolean isClientVisible() { @@ -257,5 +278,5 @@ class InsetsSourceProvider { @Override public void writeToProto(ProtoOutputStream proto) { } - }; + } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index bb704957a55a1..4ebb553318e8e 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -20,6 +20,8 @@ import static android.view.InsetsState.InternalInsetType; import static android.view.InsetsState.TYPE_IME; import static android.view.InsetsState.TYPE_NAVIGATION_BAR; import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; +import static android.view.ViewRootImpl.sNewInsetsMode; import android.annotation.NonNull; import android.annotation.Nullable; @@ -47,6 +49,10 @@ class InsetsStateController { private final ArrayMap> mControlTargetTypeMap = new ArrayMap<>(); private final SparseArray mTypeControlTargetMap = new SparseArray<>(); + + /** @see #onControlFakeTargetChanged */ + private final SparseArray mTypeFakeControlTargetMap = new SparseArray<>(); + private final ArraySet mPendingControlChanged = new ArraySet<>(); private final Consumer mDispatchInsetsChanged = w -> { @@ -93,7 +99,7 @@ class InsetsStateController { final int size = controlled.size(); final InsetsSourceControl[] result = new InsetsSourceControl[size]; for (int i = 0; i < size; i++) { - result[i] = mProviders.get(controlled.get(i)).getControl(); + result[i] = mProviders.get(controlled.get(i)).getControl(target); } return result; } @@ -157,7 +163,8 @@ class InsetsStateController { void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget, InsetsSourceProvider provider) { - removeFromControlMaps(previousControlTarget, provider.getSource().getType()); + removeFromControlMaps(previousControlTarget, provider.getSource().getType(), + false /* fake */); } private void onControlChanged(@InternalInsetType int type, @@ -175,17 +182,47 @@ class InsetsStateController { } provider.updateControlForTarget(target, false /* force */); if (previous != null) { - removeFromControlMaps(previous, type); + removeFromControlMaps(previous, type, false /* fake */); mPendingControlChanged.add(previous); } if (target != null) { - addToControlMaps(target, type); + addToControlMaps(target, type, false /* fake */); mPendingControlChanged.add(target); } } + /** + * The fake target saved here will be used to pretend to the app that it's still under control + * of the bars while it's not really, but we still need to find out the apps intentions around + * showing/hiding. For example, when the transient bars are showing, and the fake target + * requests to show system bars, the transient state will be aborted. + */ + void onControlFakeTargetChanged(@InternalInsetType int type, + @Nullable InsetsControlTarget fakeTarget) { + if (sNewInsetsMode != NEW_INSETS_MODE_FULL) { + return; + } + final InsetsControlTarget previous = mTypeFakeControlTargetMap.get(type); + if (fakeTarget == previous) { + return; + } + final InsetsSourceProvider provider = mProviders.get(type); + if (provider == null) { + return; + } + provider.updateControlForFakeTarget(fakeTarget); + if (previous != null) { + removeFromControlMaps(previous, type, true /* fake */); + mPendingControlChanged.add(previous); + } + if (fakeTarget != null) { + addToControlMaps(fakeTarget, type, true /* fake */); + mPendingControlChanged.add(fakeTarget); + } + } + private void removeFromControlMaps(@NonNull InsetsControlTarget target, - @InternalInsetType int type) { + @InternalInsetType int type, boolean fake) { final ArrayList array = mControlTargetTypeMap.get(target); if (array == null) { return; @@ -194,15 +231,23 @@ class InsetsStateController { if (array.isEmpty()) { mControlTargetTypeMap.remove(target); } - mTypeControlTargetMap.remove(type); + if (fake) { + mTypeFakeControlTargetMap.remove(type); + } else { + mTypeControlTargetMap.remove(type); + } } private void addToControlMaps(@NonNull InsetsControlTarget target, - @InternalInsetType int type) { + @InternalInsetType int type, boolean fake) { final ArrayList array = mControlTargetTypeMap.computeIfAbsent(target, key -> new ArrayList<>()); array.add(type); - mTypeControlTargetMap.put(type, target); + if (fake) { + mTypeFakeControlTargetMap.put(type, target); + } else { + mTypeControlTargetMap.put(type, target); + } } void notifyControlChanged(InsetsControlTarget target) { diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java index 86ee75ebf3df1..3e2e4382a68cb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -31,14 +31,15 @@ import android.platform.test.annotations.Presubmit; import android.view.InsetsSource; import android.view.InsetsState; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; @SmallTest @Presubmit +@RunWith(WindowTestRunner.class) public class InsetsSourceProviderTest extends WindowTestsBase { private InsetsSource mSource = new InsetsSource(TYPE_TOP_BAR); @@ -53,7 +54,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testPostLayout() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); topBar.getFrameLw().set(0, 0, 500, 100); topBar.mHasSurface = true; mProvider.setWindow(topBar, null); @@ -66,7 +67,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testPostLayout_invisible() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); topBar.getFrameLw().set(0, 0, 500, 100); mProvider.setWindow(topBar, null); mProvider.onPostLayout(); @@ -76,7 +77,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testPostLayout_frameProvider() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); topBar.getFrameLw().set(0, 0, 500, 100); mProvider.setWindow(topBar, (displayFrames, windowState, rect) -> { @@ -88,19 +89,32 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testUpdateControlForTarget() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); topBar.getFrameLw().set(0, 0, 500, 100); mProvider.setWindow(topBar, null); mProvider.updateControlForTarget(target, false /* force */); - assertNotNull(mProvider.getControl()); + assertNotNull(mProvider.getControl(target)); mProvider.updateControlForTarget(null, false /* force */); - assertNull(mProvider.getControl()); + assertNull(mProvider.getControl(target)); + } + + @Test + public void testUpdateControlForFakeTarget() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); + final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); + topBar.getFrameLw().set(0, 0, 500, 100); + mProvider.setWindow(topBar, null); + mProvider.updateControlForFakeTarget(target); + assertNotNull(mProvider.getControl(target)); + assertNull(mProvider.getControl(target).getLeash()); + mProvider.updateControlForFakeTarget(null); + assertNull(mProvider.getControl(target)); } @Test public void testInsetsModified() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); topBar.getFrameLw().set(0, 0, 500, 100); mProvider.setWindow(topBar, null); @@ -113,7 +127,7 @@ public class InsetsSourceProviderTest extends WindowTestsBase { @Test public void testInsetsModified_noControl() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); final WindowState target = createWindow(null, TYPE_APPLICATION, "target"); topBar.getFrameLw().set(0, 0, 500, 100); mProvider.setWindow(topBar, null);