Loading core/java/android/window/WindowContainerTransaction.java +28 −3 Original line number Diff line number Diff line Loading @@ -688,6 +688,7 @@ public final class WindowContainerTransaction implements Parcelable { .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type) .setSource(InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE) .setArbitraryRectangle(frame)) .setInsetsFrameOwner(owner) .build(); mHierarchyOps.add(hierarchyOp); return this; Loading @@ -712,6 +713,7 @@ public final class WindowContainerTransaction implements Parcelable { new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER) .setContainer(receiver.asBinder()) .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type)) .setInsetsFrameOwner(owner) .build(); mHierarchyOps.add(hierarchyOp); return this; Loading Loading @@ -1344,8 +1346,12 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private IBinder mReparent; @Nullable private InsetsFrameProvider mInsetsFrameProvider; @Nullable private IBinder mInsetsFrameOwner; // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom. private boolean mToTop; Loading Loading @@ -1478,6 +1484,7 @@ public final class WindowContainerTransaction implements Parcelable { mContainer = copy.mContainer; mReparent = copy.mReparent; mInsetsFrameProvider = copy.mInsetsFrameProvider; mInsetsFrameOwner = copy.mInsetsFrameOwner; mToTop = copy.mToTop; mReparentTopOnly = copy.mReparentTopOnly; mWindowingModes = copy.mWindowingModes; Loading @@ -1496,6 +1503,7 @@ public final class WindowContainerTransaction implements Parcelable { mContainer = in.readStrongBinder(); mReparent = in.readStrongBinder(); mInsetsFrameProvider = in.readTypedObject(InsetsFrameProvider.CREATOR); mInsetsFrameOwner = in.readStrongBinder(); mToTop = in.readBoolean(); mReparentTopOnly = in.readBoolean(); mWindowingModes = in.createIntArray(); Loading Loading @@ -1527,6 +1535,11 @@ public final class WindowContainerTransaction implements Parcelable { return mInsetsFrameProvider; } @Nullable public IBinder getInsetsFrameOwner() { return mInsetsFrameOwner; } @NonNull public IBinder getContainer() { return mContainer; Loading Loading @@ -1657,7 +1670,8 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER: sb.append("container=").append(mContainer) .append(" provider=").append(mInsetsFrameProvider); .append(" provider=").append(mInsetsFrameProvider) .append(" owner=").append(mInsetsFrameOwner); break; case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: sb.append("container=").append(mContainer) Loading Loading @@ -1697,6 +1711,7 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeStrongBinder(mContainer); dest.writeStrongBinder(mReparent); dest.writeTypedObject(mInsetsFrameProvider, flags); dest.writeStrongBinder(mInsetsFrameOwner); dest.writeBoolean(mToTop); dest.writeBoolean(mReparentTopOnly); dest.writeIntArray(mWindowingModes); Loading Loading @@ -1737,8 +1752,12 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private IBinder mReparent; @Nullable private InsetsFrameProvider mInsetsFrameProvider; @Nullable private IBinder mInsetsFrameOwner; private boolean mToTop; private boolean mReparentTopOnly; Loading Loading @@ -1782,8 +1801,13 @@ public final class WindowContainerTransaction implements Parcelable { return this; } Builder setInsetsFrameProvider(InsetsFrameProvider providers) { mInsetsFrameProvider = providers; Builder setInsetsFrameProvider(InsetsFrameProvider provider) { mInsetsFrameProvider = provider; return this; } Builder setInsetsFrameOwner(IBinder owner) { mInsetsFrameOwner = owner; return this; } Loading Loading @@ -1854,6 +1878,7 @@ public final class WindowContainerTransaction implements Parcelable { ? Arrays.copyOf(mActivityTypes, mActivityTypes.length) : null; hierarchyOp.mInsetsFrameProvider = mInsetsFrameProvider; hierarchyOp.mInsetsFrameOwner = mInsetsFrameOwner; hierarchyOp.mToTop = mToTop; hierarchyOp.mReparentTopOnly = mReparentTopOnly; hierarchyOp.mLaunchOptions = mLaunchOptions; Loading services/core/java/com/android/server/wm/WindowContainer.java +90 −18 Original line number Diff line number Diff line Loading @@ -81,7 +81,9 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; import android.os.Trace; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Pair; import android.util.Pools; Loading Loading @@ -174,6 +176,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ protected SparseArray<InsetsSourceProvider> mInsetsSourceProviders = null; @Nullable private ArrayMap<IBinder, DeathRecipient> mInsetsOwnerDeathRecipientMap; // List of children for this window container. List is in z-order as the children appear on // screen with the top-most window container at the tail of the list. protected final WindowList<E> mChildren = new WindowList<E>(); Loading Loading @@ -419,11 +424,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * Adds an {@link InsetsFrameProvider} which describes what insets should be provided to * this {@link WindowContainer} and its children. * * @param provider describes the insets types and the frames. * @param provider describes the insets type and the frame. * @param owner owns the insets source which only exists when the owner is alive. */ void addLocalInsetsFrameProvider(InsetsFrameProvider provider) { if (provider == null) { throw new IllegalArgumentException("Insets type not specified."); void addLocalInsetsFrameProvider(InsetsFrameProvider provider, IBinder owner) { if (provider == null || owner == null) { throw new IllegalArgumentException("Insets provider or owner not specified."); } if (mDisplayContent == null) { // This is possible this container is detached when WM shell is responding to a previous Loading @@ -432,10 +438,26 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< Slog.w(TAG, "Can't add insets frame provider when detached. " + this); return; } if (mInsetsOwnerDeathRecipientMap == null) { mInsetsOwnerDeathRecipientMap = new ArrayMap<>(); } DeathRecipient deathRecipient = mInsetsOwnerDeathRecipientMap.get(owner); if (deathRecipient == null) { deathRecipient = new DeathRecipient(owner); try { owner.linkToDeath(deathRecipient, 0); } catch (RemoteException e) { Slog.w(TAG, "Failed to add source for " + provider + " since the owner has died."); return; } mInsetsOwnerDeathRecipientMap.put(owner, deathRecipient); } final int id = provider.getId(); deathRecipient.addSourceId(id); if (mLocalInsetsSources == null) { mLocalInsetsSources = new SparseArray<>(); } final int id = provider.getId(); if (mLocalInsetsSources.get(id) != null) { if (DEBUG) { Slog.d(TAG, "The local insets source for this " + provider Loading @@ -448,27 +470,77 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mDisplayContent.getInsetsStateController().updateAboveInsetsState(true); } void removeLocalInsetsFrameProvider(InsetsFrameProvider provider) { if (provider == null) { throw new IllegalArgumentException("Insets type not specified."); private class DeathRecipient implements IBinder.DeathRecipient { private final IBinder mOwner; private final ArraySet<Integer> mSourceIds = new ArraySet<>(); DeathRecipient(IBinder owner) { mOwner = owner; } void addSourceId(int id) { mSourceIds.add(id); } void removeSourceId(int id) { mSourceIds.remove(id); } boolean hasSource() { return !mSourceIds.isEmpty(); } @Override public void binderDied() { synchronized (mWmService.mGlobalLock) { boolean changed = false; for (int i = mSourceIds.size() - 1; i >= 0; i--) { changed |= removeLocalInsetsSource(mSourceIds.valueAt(i)); } mSourceIds.clear(); mOwner.unlinkToDeath(this, 0); mInsetsOwnerDeathRecipientMap.remove(mOwner); if (changed && mDisplayContent != null) { mDisplayContent.getInsetsStateController().updateAboveInsetsState(true); } } } if (mLocalInsetsSources == null) { return; } void removeLocalInsetsFrameProvider(InsetsFrameProvider provider, IBinder owner) { if (provider == null || owner == null) { throw new IllegalArgumentException("Insets provider or owner not specified."); } final int id = provider.getId(); if (mLocalInsetsSources.get(id) == null) { if (DEBUG) { Slog.d(TAG, "Given " + provider + " doesn't have a local insets source."); if (removeLocalInsetsSource(id) && mDisplayContent != null) { mDisplayContent.getInsetsStateController().updateAboveInsetsState(true); } if (mInsetsOwnerDeathRecipientMap == null) { return; } mLocalInsetsSources.remove(id); final DeathRecipient deathRecipient = mInsetsOwnerDeathRecipientMap.get(owner); if (deathRecipient == null) { return; } deathRecipient.removeSourceId(id); if (!deathRecipient.hasSource()) { owner.unlinkToDeath(deathRecipient, 0); mInsetsOwnerDeathRecipientMap.remove(owner); } } // Update insets if this window is attached. if (mDisplayContent != null) { mDisplayContent.getInsetsStateController().updateAboveInsetsState(true); private boolean removeLocalInsetsSource(int id) { if (mLocalInsetsSources == null) { return false; } if (mLocalInsetsSources.removeReturnOld(id) == null) { if (DEBUG) { Slog.d(TAG, "Given id " + Integer.toHexString(id) + " doesn't exist."); } return false; } return true; } /** Loading services/core/java/com/android/server/wm/WindowOrganizerController.java +4 −2 Original line number Diff line number Diff line Loading @@ -1090,7 +1090,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub + container); break; } container.addLocalInsetsFrameProvider(hop.getInsetsFrameProvider()); container.addLocalInsetsFrameProvider( hop.getInsetsFrameProvider(), hop.getInsetsFrameOwner()); break; } case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER: { Loading @@ -1100,7 +1101,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub + container); break; } container.removeLocalInsetsFrameProvider(hop.getInsetsFrameProvider()); container.removeLocalInsetsFrameProvider( hop.getInsetsFrameProvider(), hop.getInsetsFrameOwner()); break; } case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: { Loading services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +169 −16 Original line number Diff line number Diff line Loading @@ -64,10 +64,19 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.clearInvocations; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.os.DeadObjectException; import android.os.IBinder; import android.os.IInterface; import android.os.Parcel; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.platform.test.annotations.Presubmit; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; Loading @@ -87,8 +96,10 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import java.io.FileDescriptor; import java.util.ArrayList; import java.util.Comparator; import java.util.NoSuchElementException; /** Loading Loading @@ -1404,7 +1415,7 @@ public class WindowContainerTests extends WindowTestsBase { } @Test public void testAddLocalInsetsSourceProvider() { public void testAddLocalInsetsFrameProvider() { /* ___ rootTask _______________________________________________ | | | | Loading Loading @@ -1435,19 +1446,20 @@ public class WindowContainerTests extends WindowTestsBase { TYPE_BASE_APPLICATION); attrs2.setTitle("AppWindow2"); activity2.addWindow(createWindowState(attrs2, activity2)); final Binder owner = new Binder(); Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700); Rect genericOverlayInsetsRect2 = new Rect(0, 0, 1080, 200); final InsetsFrameProvider provider1 = new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(genericOverlayInsetsRect1); final InsetsFrameProvider provider2 = new InsetsFrameProvider(null, 2, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 2, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(genericOverlayInsetsRect2); final int sourceId1 = provider1.getId(); final int sourceId2 = provider2.getId(); rootTask.addLocalInsetsFrameProvider(provider1); container.addLocalInsetsFrameProvider(provider2); rootTask.addLocalInsetsFrameProvider(provider1, owner); container.addLocalInsetsFrameProvider(provider2, owner); InsetsSource genericOverlayInsetsProvider1Source = new InsetsSource( sourceId1, systemOverlays()); Loading Loading @@ -1479,7 +1491,7 @@ public class WindowContainerTests extends WindowTestsBase { } @Test public void testAddLocalInsetsSourceProvider_sameType_replacesInsets() { public void testAddLocalInsetsFrameProvider_sameType_replacesInsets() { /* ___ rootTask ________________________________________ | | | Loading @@ -1494,24 +1506,25 @@ public class WindowContainerTests extends WindowTestsBase { attrs.setTitle("AppWindow0"); activity0.addWindow(createWindowState(attrs, activity0)); final Binder owner = new Binder(); final Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700); final Rect genericOverlayInsetsRect2 = new Rect(0, 0, 1080, 200); final InsetsFrameProvider provider1 = new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(genericOverlayInsetsRect1); final InsetsFrameProvider provider2 = new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(genericOverlayInsetsRect2); final int sourceId1 = provider1.getId(); final int sourceId2 = provider2.getId(); rootTask.addLocalInsetsFrameProvider(provider1); rootTask.addLocalInsetsFrameProvider(provider1, owner); activity0.forAllWindows(window -> { assertEquals(genericOverlayInsetsRect1, window.getInsetsState().peekSource(sourceId1).getFrame()); }, true); rootTask.addLocalInsetsFrameProvider(provider2); rootTask.addLocalInsetsFrameProvider(provider2, owner); activity0.forAllWindows(window -> { assertEquals(genericOverlayInsetsRect2, Loading @@ -1520,7 +1533,7 @@ public class WindowContainerTests extends WindowTestsBase { } @Test public void testRemoveLocalInsetsSourceProvider() { public void testRemoveLocalInsetsFrameProvider() { /* ___ rootTask _______________________________________________ | | | | Loading Loading @@ -1554,21 +1567,22 @@ public class WindowContainerTests extends WindowTestsBase { activity2.addWindow(createWindowState(attrs2, activity2)); final Binder owner = new Binder(); final Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700); final Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200); final InsetsFrameProvider provider1 = new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(navigationBarInsetsRect1); final InsetsFrameProvider provider2 = new InsetsFrameProvider(null, 2, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 2, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(navigationBarInsetsRect2); final int sourceId1 = provider1.getId(); final int sourceId2 = provider2.getId(); rootTask.addLocalInsetsFrameProvider(provider1); container.addLocalInsetsFrameProvider(provider2); rootTask.addLocalInsetsFrameProvider(provider1, owner); container.addLocalInsetsFrameProvider(provider2, owner); mDisplayContent.getInsetsStateController().onPostLayout(); rootTask.removeLocalInsetsFrameProvider(provider1); rootTask.removeLocalInsetsFrameProvider(provider1, owner); mDisplayContent.getInsetsStateController().onPostLayout(); activity0.forAllWindows(window -> { Loading @@ -1593,6 +1607,67 @@ public class WindowContainerTests extends WindowTestsBase { }, true); } @Test public void testAddLocalInsetsFrameProvider_ownerDiesAfterAdding() { final Task task = createTask(mDisplayContent); final TestBinder owner = new TestBinder(); final InsetsFrameProvider provider = new InsetsFrameProvider(owner, 0, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(new Rect()); task.addLocalInsetsFrameProvider(provider, owner); assertTrue("The death recipient must exist.", owner.hasDeathRecipient()); assertTrue("The source must be added.", hasLocalSource(task, provider.getId())); // The owner dies after adding the source. owner.die(); assertFalse("The death recipient must be removed.", owner.hasDeathRecipient()); assertFalse("The source must be removed.", hasLocalSource(task, provider.getId())); } @Test public void testAddLocalInsetsFrameProvider_ownerDiesBeforeAdding() { final Task task = createTask(mDisplayContent); final TestBinder owner = new TestBinder(); // The owner dies before adding the source. owner.die(); final InsetsFrameProvider provider = new InsetsFrameProvider(owner, 0, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(new Rect()); task.addLocalInsetsFrameProvider(provider, owner); assertFalse("The death recipient must not exist.", owner.hasDeathRecipient()); assertFalse("The source must not be added.", hasLocalSource(task, provider.getId())); } @Test public void testRemoveLocalInsetsFrameProvider_removeDeathRecipient() { final Task task = createTask(mDisplayContent); final TestBinder owner = new TestBinder(); final InsetsFrameProvider provider = new InsetsFrameProvider(owner, 0, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(new Rect()); task.addLocalInsetsFrameProvider(provider, owner); assertTrue("The death recipient must exist.", owner.hasDeathRecipient()); assertTrue("The source must be added.", hasLocalSource(task, provider.getId())); task.removeLocalInsetsFrameProvider(provider, owner); assertFalse("The death recipient must be removed.", owner.hasDeathRecipient()); assertFalse("The source must be removed.", hasLocalSource(task, provider.getId())); } private static boolean hasLocalSource(WindowContainer container, int sourceId) { if (container.mLocalInsetsSources == null) { return false; } return container.mLocalInsetsSources.contains(sourceId); } /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; Loading Loading @@ -1797,4 +1872,82 @@ public class WindowContainerTests extends WindowTestsBase { mIsVisibleRequested = isVisibleRequested; } } private static class TestBinder implements IBinder { private boolean mDead; private final ArrayList<IBinder.DeathRecipient> mDeathRecipients = new ArrayList<>(); public void die() { mDead = true; for (int i = mDeathRecipients.size() - 1; i >= 0; i--) { final DeathRecipient recipient = mDeathRecipients.get(i); recipient.binderDied(this); } } public boolean hasDeathRecipient() { return !mDeathRecipients.isEmpty(); } @Override public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException { if (mDead) { throw new DeadObjectException(); } mDeathRecipients.add(recipient); } @Override public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) { final boolean successes = mDeathRecipients.remove(recipient); if (successes || mDead) { return successes; } throw new NoSuchElementException("Given recipient has not been registered."); } @Override public boolean isBinderAlive() { return !mDead; } @Override public boolean pingBinder() { return !mDead; } @Nullable @Override public String getInterfaceDescriptor() throws RemoteException { return null; } @Nullable @Override public IInterface queryLocalInterface(@NonNull String descriptor) { return null; } @Override public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException { } @Override public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException { } @Override public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback shellCallback, @NonNull ResultReceiver resultReceiver) throws RemoteException { } @Override public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { return false; } } } Loading
core/java/android/window/WindowContainerTransaction.java +28 −3 Original line number Diff line number Diff line Loading @@ -688,6 +688,7 @@ public final class WindowContainerTransaction implements Parcelable { .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type) .setSource(InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE) .setArbitraryRectangle(frame)) .setInsetsFrameOwner(owner) .build(); mHierarchyOps.add(hierarchyOp); return this; Loading @@ -712,6 +713,7 @@ public final class WindowContainerTransaction implements Parcelable { new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER) .setContainer(receiver.asBinder()) .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type)) .setInsetsFrameOwner(owner) .build(); mHierarchyOps.add(hierarchyOp); return this; Loading Loading @@ -1344,8 +1346,12 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private IBinder mReparent; @Nullable private InsetsFrameProvider mInsetsFrameProvider; @Nullable private IBinder mInsetsFrameOwner; // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom. private boolean mToTop; Loading Loading @@ -1478,6 +1484,7 @@ public final class WindowContainerTransaction implements Parcelable { mContainer = copy.mContainer; mReparent = copy.mReparent; mInsetsFrameProvider = copy.mInsetsFrameProvider; mInsetsFrameOwner = copy.mInsetsFrameOwner; mToTop = copy.mToTop; mReparentTopOnly = copy.mReparentTopOnly; mWindowingModes = copy.mWindowingModes; Loading @@ -1496,6 +1503,7 @@ public final class WindowContainerTransaction implements Parcelable { mContainer = in.readStrongBinder(); mReparent = in.readStrongBinder(); mInsetsFrameProvider = in.readTypedObject(InsetsFrameProvider.CREATOR); mInsetsFrameOwner = in.readStrongBinder(); mToTop = in.readBoolean(); mReparentTopOnly = in.readBoolean(); mWindowingModes = in.createIntArray(); Loading Loading @@ -1527,6 +1535,11 @@ public final class WindowContainerTransaction implements Parcelable { return mInsetsFrameProvider; } @Nullable public IBinder getInsetsFrameOwner() { return mInsetsFrameOwner; } @NonNull public IBinder getContainer() { return mContainer; Loading Loading @@ -1657,7 +1670,8 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER: sb.append("container=").append(mContainer) .append(" provider=").append(mInsetsFrameProvider); .append(" provider=").append(mInsetsFrameProvider) .append(" owner=").append(mInsetsFrameOwner); break; case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: sb.append("container=").append(mContainer) Loading Loading @@ -1697,6 +1711,7 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeStrongBinder(mContainer); dest.writeStrongBinder(mReparent); dest.writeTypedObject(mInsetsFrameProvider, flags); dest.writeStrongBinder(mInsetsFrameOwner); dest.writeBoolean(mToTop); dest.writeBoolean(mReparentTopOnly); dest.writeIntArray(mWindowingModes); Loading Loading @@ -1737,8 +1752,12 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private IBinder mReparent; @Nullable private InsetsFrameProvider mInsetsFrameProvider; @Nullable private IBinder mInsetsFrameOwner; private boolean mToTop; private boolean mReparentTopOnly; Loading Loading @@ -1782,8 +1801,13 @@ public final class WindowContainerTransaction implements Parcelable { return this; } Builder setInsetsFrameProvider(InsetsFrameProvider providers) { mInsetsFrameProvider = providers; Builder setInsetsFrameProvider(InsetsFrameProvider provider) { mInsetsFrameProvider = provider; return this; } Builder setInsetsFrameOwner(IBinder owner) { mInsetsFrameOwner = owner; return this; } Loading Loading @@ -1854,6 +1878,7 @@ public final class WindowContainerTransaction implements Parcelable { ? Arrays.copyOf(mActivityTypes, mActivityTypes.length) : null; hierarchyOp.mInsetsFrameProvider = mInsetsFrameProvider; hierarchyOp.mInsetsFrameOwner = mInsetsFrameOwner; hierarchyOp.mToTop = mToTop; hierarchyOp.mReparentTopOnly = mReparentTopOnly; hierarchyOp.mLaunchOptions = mLaunchOptions; Loading
services/core/java/com/android/server/wm/WindowContainer.java +90 −18 Original line number Diff line number Diff line Loading @@ -81,7 +81,9 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; import android.os.Trace; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Pair; import android.util.Pools; Loading Loading @@ -174,6 +176,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ protected SparseArray<InsetsSourceProvider> mInsetsSourceProviders = null; @Nullable private ArrayMap<IBinder, DeathRecipient> mInsetsOwnerDeathRecipientMap; // List of children for this window container. List is in z-order as the children appear on // screen with the top-most window container at the tail of the list. protected final WindowList<E> mChildren = new WindowList<E>(); Loading Loading @@ -419,11 +424,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * Adds an {@link InsetsFrameProvider} which describes what insets should be provided to * this {@link WindowContainer} and its children. * * @param provider describes the insets types and the frames. * @param provider describes the insets type and the frame. * @param owner owns the insets source which only exists when the owner is alive. */ void addLocalInsetsFrameProvider(InsetsFrameProvider provider) { if (provider == null) { throw new IllegalArgumentException("Insets type not specified."); void addLocalInsetsFrameProvider(InsetsFrameProvider provider, IBinder owner) { if (provider == null || owner == null) { throw new IllegalArgumentException("Insets provider or owner not specified."); } if (mDisplayContent == null) { // This is possible this container is detached when WM shell is responding to a previous Loading @@ -432,10 +438,26 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< Slog.w(TAG, "Can't add insets frame provider when detached. " + this); return; } if (mInsetsOwnerDeathRecipientMap == null) { mInsetsOwnerDeathRecipientMap = new ArrayMap<>(); } DeathRecipient deathRecipient = mInsetsOwnerDeathRecipientMap.get(owner); if (deathRecipient == null) { deathRecipient = new DeathRecipient(owner); try { owner.linkToDeath(deathRecipient, 0); } catch (RemoteException e) { Slog.w(TAG, "Failed to add source for " + provider + " since the owner has died."); return; } mInsetsOwnerDeathRecipientMap.put(owner, deathRecipient); } final int id = provider.getId(); deathRecipient.addSourceId(id); if (mLocalInsetsSources == null) { mLocalInsetsSources = new SparseArray<>(); } final int id = provider.getId(); if (mLocalInsetsSources.get(id) != null) { if (DEBUG) { Slog.d(TAG, "The local insets source for this " + provider Loading @@ -448,27 +470,77 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mDisplayContent.getInsetsStateController().updateAboveInsetsState(true); } void removeLocalInsetsFrameProvider(InsetsFrameProvider provider) { if (provider == null) { throw new IllegalArgumentException("Insets type not specified."); private class DeathRecipient implements IBinder.DeathRecipient { private final IBinder mOwner; private final ArraySet<Integer> mSourceIds = new ArraySet<>(); DeathRecipient(IBinder owner) { mOwner = owner; } void addSourceId(int id) { mSourceIds.add(id); } void removeSourceId(int id) { mSourceIds.remove(id); } boolean hasSource() { return !mSourceIds.isEmpty(); } @Override public void binderDied() { synchronized (mWmService.mGlobalLock) { boolean changed = false; for (int i = mSourceIds.size() - 1; i >= 0; i--) { changed |= removeLocalInsetsSource(mSourceIds.valueAt(i)); } mSourceIds.clear(); mOwner.unlinkToDeath(this, 0); mInsetsOwnerDeathRecipientMap.remove(mOwner); if (changed && mDisplayContent != null) { mDisplayContent.getInsetsStateController().updateAboveInsetsState(true); } } } if (mLocalInsetsSources == null) { return; } void removeLocalInsetsFrameProvider(InsetsFrameProvider provider, IBinder owner) { if (provider == null || owner == null) { throw new IllegalArgumentException("Insets provider or owner not specified."); } final int id = provider.getId(); if (mLocalInsetsSources.get(id) == null) { if (DEBUG) { Slog.d(TAG, "Given " + provider + " doesn't have a local insets source."); if (removeLocalInsetsSource(id) && mDisplayContent != null) { mDisplayContent.getInsetsStateController().updateAboveInsetsState(true); } if (mInsetsOwnerDeathRecipientMap == null) { return; } mLocalInsetsSources.remove(id); final DeathRecipient deathRecipient = mInsetsOwnerDeathRecipientMap.get(owner); if (deathRecipient == null) { return; } deathRecipient.removeSourceId(id); if (!deathRecipient.hasSource()) { owner.unlinkToDeath(deathRecipient, 0); mInsetsOwnerDeathRecipientMap.remove(owner); } } // Update insets if this window is attached. if (mDisplayContent != null) { mDisplayContent.getInsetsStateController().updateAboveInsetsState(true); private boolean removeLocalInsetsSource(int id) { if (mLocalInsetsSources == null) { return false; } if (mLocalInsetsSources.removeReturnOld(id) == null) { if (DEBUG) { Slog.d(TAG, "Given id " + Integer.toHexString(id) + " doesn't exist."); } return false; } return true; } /** Loading
services/core/java/com/android/server/wm/WindowOrganizerController.java +4 −2 Original line number Diff line number Diff line Loading @@ -1090,7 +1090,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub + container); break; } container.addLocalInsetsFrameProvider(hop.getInsetsFrameProvider()); container.addLocalInsetsFrameProvider( hop.getInsetsFrameProvider(), hop.getInsetsFrameOwner()); break; } case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER: { Loading @@ -1100,7 +1101,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub + container); break; } container.removeLocalInsetsFrameProvider(hop.getInsetsFrameProvider()); container.removeLocalInsetsFrameProvider( hop.getInsetsFrameProvider(), hop.getInsetsFrameOwner()); break; } case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: { Loading
services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +169 −16 Original line number Diff line number Diff line Loading @@ -64,10 +64,19 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.clearInvocations; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.os.DeadObjectException; import android.os.IBinder; import android.os.IInterface; import android.os.Parcel; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.platform.test.annotations.Presubmit; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; Loading @@ -87,8 +96,10 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import java.io.FileDescriptor; import java.util.ArrayList; import java.util.Comparator; import java.util.NoSuchElementException; /** Loading Loading @@ -1404,7 +1415,7 @@ public class WindowContainerTests extends WindowTestsBase { } @Test public void testAddLocalInsetsSourceProvider() { public void testAddLocalInsetsFrameProvider() { /* ___ rootTask _______________________________________________ | | | | Loading Loading @@ -1435,19 +1446,20 @@ public class WindowContainerTests extends WindowTestsBase { TYPE_BASE_APPLICATION); attrs2.setTitle("AppWindow2"); activity2.addWindow(createWindowState(attrs2, activity2)); final Binder owner = new Binder(); Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700); Rect genericOverlayInsetsRect2 = new Rect(0, 0, 1080, 200); final InsetsFrameProvider provider1 = new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(genericOverlayInsetsRect1); final InsetsFrameProvider provider2 = new InsetsFrameProvider(null, 2, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 2, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(genericOverlayInsetsRect2); final int sourceId1 = provider1.getId(); final int sourceId2 = provider2.getId(); rootTask.addLocalInsetsFrameProvider(provider1); container.addLocalInsetsFrameProvider(provider2); rootTask.addLocalInsetsFrameProvider(provider1, owner); container.addLocalInsetsFrameProvider(provider2, owner); InsetsSource genericOverlayInsetsProvider1Source = new InsetsSource( sourceId1, systemOverlays()); Loading Loading @@ -1479,7 +1491,7 @@ public class WindowContainerTests extends WindowTestsBase { } @Test public void testAddLocalInsetsSourceProvider_sameType_replacesInsets() { public void testAddLocalInsetsFrameProvider_sameType_replacesInsets() { /* ___ rootTask ________________________________________ | | | Loading @@ -1494,24 +1506,25 @@ public class WindowContainerTests extends WindowTestsBase { attrs.setTitle("AppWindow0"); activity0.addWindow(createWindowState(attrs, activity0)); final Binder owner = new Binder(); final Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700); final Rect genericOverlayInsetsRect2 = new Rect(0, 0, 1080, 200); final InsetsFrameProvider provider1 = new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(genericOverlayInsetsRect1); final InsetsFrameProvider provider2 = new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(genericOverlayInsetsRect2); final int sourceId1 = provider1.getId(); final int sourceId2 = provider2.getId(); rootTask.addLocalInsetsFrameProvider(provider1); rootTask.addLocalInsetsFrameProvider(provider1, owner); activity0.forAllWindows(window -> { assertEquals(genericOverlayInsetsRect1, window.getInsetsState().peekSource(sourceId1).getFrame()); }, true); rootTask.addLocalInsetsFrameProvider(provider2); rootTask.addLocalInsetsFrameProvider(provider2, owner); activity0.forAllWindows(window -> { assertEquals(genericOverlayInsetsRect2, Loading @@ -1520,7 +1533,7 @@ public class WindowContainerTests extends WindowTestsBase { } @Test public void testRemoveLocalInsetsSourceProvider() { public void testRemoveLocalInsetsFrameProvider() { /* ___ rootTask _______________________________________________ | | | | Loading Loading @@ -1554,21 +1567,22 @@ public class WindowContainerTests extends WindowTestsBase { activity2.addWindow(createWindowState(attrs2, activity2)); final Binder owner = new Binder(); final Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700); final Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200); final InsetsFrameProvider provider1 = new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(navigationBarInsetsRect1); final InsetsFrameProvider provider2 = new InsetsFrameProvider(null, 2, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 2, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(navigationBarInsetsRect2); final int sourceId1 = provider1.getId(); final int sourceId2 = provider2.getId(); rootTask.addLocalInsetsFrameProvider(provider1); container.addLocalInsetsFrameProvider(provider2); rootTask.addLocalInsetsFrameProvider(provider1, owner); container.addLocalInsetsFrameProvider(provider2, owner); mDisplayContent.getInsetsStateController().onPostLayout(); rootTask.removeLocalInsetsFrameProvider(provider1); rootTask.removeLocalInsetsFrameProvider(provider1, owner); mDisplayContent.getInsetsStateController().onPostLayout(); activity0.forAllWindows(window -> { Loading @@ -1593,6 +1607,67 @@ public class WindowContainerTests extends WindowTestsBase { }, true); } @Test public void testAddLocalInsetsFrameProvider_ownerDiesAfterAdding() { final Task task = createTask(mDisplayContent); final TestBinder owner = new TestBinder(); final InsetsFrameProvider provider = new InsetsFrameProvider(owner, 0, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(new Rect()); task.addLocalInsetsFrameProvider(provider, owner); assertTrue("The death recipient must exist.", owner.hasDeathRecipient()); assertTrue("The source must be added.", hasLocalSource(task, provider.getId())); // The owner dies after adding the source. owner.die(); assertFalse("The death recipient must be removed.", owner.hasDeathRecipient()); assertFalse("The source must be removed.", hasLocalSource(task, provider.getId())); } @Test public void testAddLocalInsetsFrameProvider_ownerDiesBeforeAdding() { final Task task = createTask(mDisplayContent); final TestBinder owner = new TestBinder(); // The owner dies before adding the source. owner.die(); final InsetsFrameProvider provider = new InsetsFrameProvider(owner, 0, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(new Rect()); task.addLocalInsetsFrameProvider(provider, owner); assertFalse("The death recipient must not exist.", owner.hasDeathRecipient()); assertFalse("The source must not be added.", hasLocalSource(task, provider.getId())); } @Test public void testRemoveLocalInsetsFrameProvider_removeDeathRecipient() { final Task task = createTask(mDisplayContent); final TestBinder owner = new TestBinder(); final InsetsFrameProvider provider = new InsetsFrameProvider(owner, 0, WindowInsets.Type.systemOverlays()) .setArbitraryRectangle(new Rect()); task.addLocalInsetsFrameProvider(provider, owner); assertTrue("The death recipient must exist.", owner.hasDeathRecipient()); assertTrue("The source must be added.", hasLocalSource(task, provider.getId())); task.removeLocalInsetsFrameProvider(provider, owner); assertFalse("The death recipient must be removed.", owner.hasDeathRecipient()); assertFalse("The source must be removed.", hasLocalSource(task, provider.getId())); } private static boolean hasLocalSource(WindowContainer container, int sourceId) { if (container.mLocalInsetsSources == null) { return false; } return container.mLocalInsetsSources.contains(sourceId); } /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; Loading Loading @@ -1797,4 +1872,82 @@ public class WindowContainerTests extends WindowTestsBase { mIsVisibleRequested = isVisibleRequested; } } private static class TestBinder implements IBinder { private boolean mDead; private final ArrayList<IBinder.DeathRecipient> mDeathRecipients = new ArrayList<>(); public void die() { mDead = true; for (int i = mDeathRecipients.size() - 1; i >= 0; i--) { final DeathRecipient recipient = mDeathRecipients.get(i); recipient.binderDied(this); } } public boolean hasDeathRecipient() { return !mDeathRecipients.isEmpty(); } @Override public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException { if (mDead) { throw new DeadObjectException(); } mDeathRecipients.add(recipient); } @Override public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) { final boolean successes = mDeathRecipients.remove(recipient); if (successes || mDead) { return successes; } throw new NoSuchElementException("Given recipient has not been registered."); } @Override public boolean isBinderAlive() { return !mDead; } @Override public boolean pingBinder() { return !mDead; } @Nullable @Override public String getInterfaceDescriptor() throws RemoteException { return null; } @Nullable @Override public IInterface queryLocalInterface(@NonNull String descriptor) { return null; } @Override public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException { } @Override public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException { } @Override public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback shellCallback, @NonNull ResultReceiver resultReceiver) throws RemoteException { } @Override public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { return false; } } }