Merge "Remove the local insets sources if the owner dies" into udc-qpr-dev

This commit is contained in:
Tiger Huang
2023-07-07 13:46:07 +00:00
committed by Android (Google) Code Review
4 changed files with 292 additions and 40 deletions

View File

@@ -688,6 +688,7 @@ public final class WindowContainerTransaction implements Parcelable {
.setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type) .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type)
.setSource(InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE) .setSource(InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE)
.setArbitraryRectangle(frame)) .setArbitraryRectangle(frame))
.setInsetsFrameOwner(owner)
.build(); .build();
mHierarchyOps.add(hierarchyOp); mHierarchyOps.add(hierarchyOp);
return this; return this;
@@ -712,6 +713,7 @@ public final class WindowContainerTransaction implements Parcelable {
new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER) new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER)
.setContainer(receiver.asBinder()) .setContainer(receiver.asBinder())
.setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type)) .setInsetsFrameProvider(new InsetsFrameProvider(owner, index, type))
.setInsetsFrameOwner(owner)
.build(); .build();
mHierarchyOps.add(hierarchyOp); mHierarchyOps.add(hierarchyOp);
return this; return this;
@@ -1344,8 +1346,12 @@ public final class WindowContainerTransaction implements Parcelable {
@Nullable @Nullable
private IBinder mReparent; private IBinder mReparent;
@Nullable
private InsetsFrameProvider mInsetsFrameProvider; private InsetsFrameProvider mInsetsFrameProvider;
@Nullable
private IBinder mInsetsFrameOwner;
// Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom. // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
private boolean mToTop; private boolean mToTop;
@@ -1478,6 +1484,7 @@ public final class WindowContainerTransaction implements Parcelable {
mContainer = copy.mContainer; mContainer = copy.mContainer;
mReparent = copy.mReparent; mReparent = copy.mReparent;
mInsetsFrameProvider = copy.mInsetsFrameProvider; mInsetsFrameProvider = copy.mInsetsFrameProvider;
mInsetsFrameOwner = copy.mInsetsFrameOwner;
mToTop = copy.mToTop; mToTop = copy.mToTop;
mReparentTopOnly = copy.mReparentTopOnly; mReparentTopOnly = copy.mReparentTopOnly;
mWindowingModes = copy.mWindowingModes; mWindowingModes = copy.mWindowingModes;
@@ -1496,6 +1503,7 @@ public final class WindowContainerTransaction implements Parcelable {
mContainer = in.readStrongBinder(); mContainer = in.readStrongBinder();
mReparent = in.readStrongBinder(); mReparent = in.readStrongBinder();
mInsetsFrameProvider = in.readTypedObject(InsetsFrameProvider.CREATOR); mInsetsFrameProvider = in.readTypedObject(InsetsFrameProvider.CREATOR);
mInsetsFrameOwner = in.readStrongBinder();
mToTop = in.readBoolean(); mToTop = in.readBoolean();
mReparentTopOnly = in.readBoolean(); mReparentTopOnly = in.readBoolean();
mWindowingModes = in.createIntArray(); mWindowingModes = in.createIntArray();
@@ -1527,6 +1535,11 @@ public final class WindowContainerTransaction implements Parcelable {
return mInsetsFrameProvider; return mInsetsFrameProvider;
} }
@Nullable
public IBinder getInsetsFrameOwner() {
return mInsetsFrameOwner;
}
@NonNull @NonNull
public IBinder getContainer() { public IBinder getContainer() {
return mContainer; return mContainer;
@@ -1657,7 +1670,8 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER:
case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER: case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER:
sb.append("container=").append(mContainer) sb.append("container=").append(mContainer)
.append(" provider=").append(mInsetsFrameProvider); .append(" provider=").append(mInsetsFrameProvider)
.append(" owner=").append(mInsetsFrameOwner);
break; break;
case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP:
sb.append("container=").append(mContainer) sb.append("container=").append(mContainer)
@@ -1697,6 +1711,7 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeStrongBinder(mContainer); dest.writeStrongBinder(mContainer);
dest.writeStrongBinder(mReparent); dest.writeStrongBinder(mReparent);
dest.writeTypedObject(mInsetsFrameProvider, flags); dest.writeTypedObject(mInsetsFrameProvider, flags);
dest.writeStrongBinder(mInsetsFrameOwner);
dest.writeBoolean(mToTop); dest.writeBoolean(mToTop);
dest.writeBoolean(mReparentTopOnly); dest.writeBoolean(mReparentTopOnly);
dest.writeIntArray(mWindowingModes); dest.writeIntArray(mWindowingModes);
@@ -1737,8 +1752,12 @@ public final class WindowContainerTransaction implements Parcelable {
@Nullable @Nullable
private IBinder mReparent; private IBinder mReparent;
@Nullable
private InsetsFrameProvider mInsetsFrameProvider; private InsetsFrameProvider mInsetsFrameProvider;
@Nullable
private IBinder mInsetsFrameOwner;
private boolean mToTop; private boolean mToTop;
private boolean mReparentTopOnly; private boolean mReparentTopOnly;
@@ -1782,8 +1801,13 @@ public final class WindowContainerTransaction implements Parcelable {
return this; return this;
} }
Builder setInsetsFrameProvider(InsetsFrameProvider providers) { Builder setInsetsFrameProvider(InsetsFrameProvider provider) {
mInsetsFrameProvider = providers; mInsetsFrameProvider = provider;
return this;
}
Builder setInsetsFrameOwner(IBinder owner) {
mInsetsFrameOwner = owner;
return this; return this;
} }
@@ -1854,6 +1878,7 @@ public final class WindowContainerTransaction implements Parcelable {
? Arrays.copyOf(mActivityTypes, mActivityTypes.length) ? Arrays.copyOf(mActivityTypes, mActivityTypes.length)
: null; : null;
hierarchyOp.mInsetsFrameProvider = mInsetsFrameProvider; hierarchyOp.mInsetsFrameProvider = mInsetsFrameProvider;
hierarchyOp.mInsetsFrameOwner = mInsetsFrameOwner;
hierarchyOp.mToTop = mToTop; hierarchyOp.mToTop = mToTop;
hierarchyOp.mReparentTopOnly = mReparentTopOnly; hierarchyOp.mReparentTopOnly = mReparentTopOnly;
hierarchyOp.mLaunchOptions = mLaunchOptions; hierarchyOp.mLaunchOptions = mLaunchOptions;

View File

@@ -81,7 +81,9 @@ import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Debug; import android.os.Debug;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace; import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Pair; import android.util.Pair;
import android.util.Pools; import android.util.Pools;
@@ -174,6 +176,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/ */
protected SparseArray<InsetsSourceProvider> mInsetsSourceProviders = null; 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 // 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. // screen with the top-most window container at the tail of the list.
protected final WindowList<E> mChildren = new WindowList<E>(); protected final WindowList<E> mChildren = new WindowList<E>();
@@ -419,11 +424,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* Adds an {@link InsetsFrameProvider} which describes what insets should be provided to * Adds an {@link InsetsFrameProvider} which describes what insets should be provided to
* this {@link WindowContainer} and its children. * 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) { void addLocalInsetsFrameProvider(InsetsFrameProvider provider, IBinder owner) {
if (provider == null) { if (provider == null || owner == null) {
throw new IllegalArgumentException("Insets type not specified."); throw new IllegalArgumentException("Insets provider or owner not specified.");
} }
if (mDisplayContent == null) { if (mDisplayContent == null) {
// This is possible this container is detached when WM shell is responding to a previous // This is possible this container is detached when WM shell is responding to a previous
@@ -432,10 +438,26 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
Slog.w(TAG, "Can't add insets frame provider when detached. " + this); Slog.w(TAG, "Can't add insets frame provider when detached. " + this);
return; 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) { if (mLocalInsetsSources == null) {
mLocalInsetsSources = new SparseArray<>(); mLocalInsetsSources = new SparseArray<>();
} }
final int id = provider.getId();
if (mLocalInsetsSources.get(id) != null) { if (mLocalInsetsSources.get(id) != null) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "The local insets source for this " + provider Slog.d(TAG, "The local insets source for this " + provider
@@ -448,28 +470,78 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mDisplayContent.getInsetsStateController().updateAboveInsetsState(true); mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
} }
void removeLocalInsetsFrameProvider(InsetsFrameProvider provider) { private class DeathRecipient implements IBinder.DeathRecipient {
if (provider == null) {
throw new IllegalArgumentException("Insets type not specified."); private final IBinder mOwner;
} private final ArraySet<Integer> mSourceIds = new ArraySet<>();
if (mLocalInsetsSources == null) {
return; DeathRecipient(IBinder owner) {
mOwner = owner;
} }
final int id = provider.getId(); void addSourceId(int id) {
if (mLocalInsetsSources.get(id) == null) { mSourceIds.add(id);
if (DEBUG) {
Slog.d(TAG, "Given " + provider + " doesn't have a local insets source.");
} }
return;
}
mLocalInsetsSources.remove(id);
// Update insets if this window is attached. void removeSourceId(int id) {
if (mDisplayContent != null) { 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); mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
} }
} }
}
}
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 (removeLocalInsetsSource(id) && mDisplayContent != null) {
mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
}
if (mInsetsOwnerDeathRecipientMap == null) {
return;
}
final DeathRecipient deathRecipient = mInsetsOwnerDeathRecipientMap.get(owner);
if (deathRecipient == null) {
return;
}
deathRecipient.removeSourceId(id);
if (!deathRecipient.hasSource()) {
owner.unlinkToDeath(deathRecipient, 0);
mInsetsOwnerDeathRecipientMap.remove(owner);
}
}
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;
}
/** /**
* Sets an {@link InsetsSourceProvider} to be associated with this {@code WindowContainer}, * Sets an {@link InsetsSourceProvider} to be associated with this {@code WindowContainer},

View File

@@ -1090,7 +1090,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
+ container); + container);
break; break;
} }
container.addLocalInsetsFrameProvider(hop.getInsetsFrameProvider()); container.addLocalInsetsFrameProvider(
hop.getInsetsFrameProvider(), hop.getInsetsFrameOwner());
break; break;
} }
case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER: { case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER: {
@@ -1100,7 +1101,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
+ container); + container);
break; break;
} }
container.removeLocalInsetsFrameProvider(hop.getInsetsFrameProvider()); container.removeLocalInsetsFrameProvider(
hop.getInsetsFrameProvider(), hop.getInsetsFrameOwner());
break; break;
} }
case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: { case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: {

View File

@@ -64,10 +64,19 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.clearInvocations;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Rect; 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.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner; import android.view.IRemoteAnimationRunner;
@@ -87,8 +96,10 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mockito; import org.mockito.Mockito;
import java.io.FileDescriptor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.NoSuchElementException;
/** /**
@@ -1404,7 +1415,7 @@ public class WindowContainerTests extends WindowTestsBase {
} }
@Test @Test
public void testAddLocalInsetsSourceProvider() { public void testAddLocalInsetsFrameProvider() {
/* /*
___ rootTask _______________________________________________ ___ rootTask _______________________________________________
| | | | | | | |
@@ -1435,19 +1446,20 @@ public class WindowContainerTests extends WindowTestsBase {
TYPE_BASE_APPLICATION); TYPE_BASE_APPLICATION);
attrs2.setTitle("AppWindow2"); attrs2.setTitle("AppWindow2");
activity2.addWindow(createWindowState(attrs2, activity2)); activity2.addWindow(createWindowState(attrs2, activity2));
final Binder owner = new Binder();
Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700); Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700);
Rect genericOverlayInsetsRect2 = new Rect(0, 0, 1080, 200); Rect genericOverlayInsetsRect2 = new Rect(0, 0, 1080, 200);
final InsetsFrameProvider provider1 = final InsetsFrameProvider provider1 =
new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays())
.setArbitraryRectangle(genericOverlayInsetsRect1); .setArbitraryRectangle(genericOverlayInsetsRect1);
final InsetsFrameProvider provider2 = final InsetsFrameProvider provider2 =
new InsetsFrameProvider(null, 2, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 2, WindowInsets.Type.systemOverlays())
.setArbitraryRectangle(genericOverlayInsetsRect2); .setArbitraryRectangle(genericOverlayInsetsRect2);
final int sourceId1 = provider1.getId(); final int sourceId1 = provider1.getId();
final int sourceId2 = provider2.getId(); final int sourceId2 = provider2.getId();
rootTask.addLocalInsetsFrameProvider(provider1); rootTask.addLocalInsetsFrameProvider(provider1, owner);
container.addLocalInsetsFrameProvider(provider2); container.addLocalInsetsFrameProvider(provider2, owner);
InsetsSource genericOverlayInsetsProvider1Source = new InsetsSource( InsetsSource genericOverlayInsetsProvider1Source = new InsetsSource(
sourceId1, systemOverlays()); sourceId1, systemOverlays());
@@ -1479,7 +1491,7 @@ public class WindowContainerTests extends WindowTestsBase {
} }
@Test @Test
public void testAddLocalInsetsSourceProvider_sameType_replacesInsets() { public void testAddLocalInsetsFrameProvider_sameType_replacesInsets() {
/* /*
___ rootTask ________________________________________ ___ rootTask ________________________________________
| | | | | |
@@ -1494,24 +1506,25 @@ public class WindowContainerTests extends WindowTestsBase {
attrs.setTitle("AppWindow0"); attrs.setTitle("AppWindow0");
activity0.addWindow(createWindowState(attrs, activity0)); activity0.addWindow(createWindowState(attrs, activity0));
final Binder owner = new Binder();
final Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700); final Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700);
final Rect genericOverlayInsetsRect2 = new Rect(0, 0, 1080, 200); final Rect genericOverlayInsetsRect2 = new Rect(0, 0, 1080, 200);
final InsetsFrameProvider provider1 = final InsetsFrameProvider provider1 =
new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays())
.setArbitraryRectangle(genericOverlayInsetsRect1); .setArbitraryRectangle(genericOverlayInsetsRect1);
final InsetsFrameProvider provider2 = final InsetsFrameProvider provider2 =
new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays())
.setArbitraryRectangle(genericOverlayInsetsRect2); .setArbitraryRectangle(genericOverlayInsetsRect2);
final int sourceId1 = provider1.getId(); final int sourceId1 = provider1.getId();
final int sourceId2 = provider2.getId(); final int sourceId2 = provider2.getId();
rootTask.addLocalInsetsFrameProvider(provider1); rootTask.addLocalInsetsFrameProvider(provider1, owner);
activity0.forAllWindows(window -> { activity0.forAllWindows(window -> {
assertEquals(genericOverlayInsetsRect1, assertEquals(genericOverlayInsetsRect1,
window.getInsetsState().peekSource(sourceId1).getFrame()); window.getInsetsState().peekSource(sourceId1).getFrame());
}, true); }, true);
rootTask.addLocalInsetsFrameProvider(provider2); rootTask.addLocalInsetsFrameProvider(provider2, owner);
activity0.forAllWindows(window -> { activity0.forAllWindows(window -> {
assertEquals(genericOverlayInsetsRect2, assertEquals(genericOverlayInsetsRect2,
@@ -1520,7 +1533,7 @@ public class WindowContainerTests extends WindowTestsBase {
} }
@Test @Test
public void testRemoveLocalInsetsSourceProvider() { public void testRemoveLocalInsetsFrameProvider() {
/* /*
___ rootTask _______________________________________________ ___ rootTask _______________________________________________
| | | | | | | |
@@ -1554,21 +1567,22 @@ public class WindowContainerTests extends WindowTestsBase {
activity2.addWindow(createWindowState(attrs2, activity2)); activity2.addWindow(createWindowState(attrs2, activity2));
final Binder owner = new Binder();
final Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700); final Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700);
final Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200); final Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200);
final InsetsFrameProvider provider1 = final InsetsFrameProvider provider1 =
new InsetsFrameProvider(null, 1, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays())
.setArbitraryRectangle(navigationBarInsetsRect1); .setArbitraryRectangle(navigationBarInsetsRect1);
final InsetsFrameProvider provider2 = final InsetsFrameProvider provider2 =
new InsetsFrameProvider(null, 2, WindowInsets.Type.systemOverlays()) new InsetsFrameProvider(owner, 2, WindowInsets.Type.systemOverlays())
.setArbitraryRectangle(navigationBarInsetsRect2); .setArbitraryRectangle(navigationBarInsetsRect2);
final int sourceId1 = provider1.getId(); final int sourceId1 = provider1.getId();
final int sourceId2 = provider2.getId(); final int sourceId2 = provider2.getId();
rootTask.addLocalInsetsFrameProvider(provider1); rootTask.addLocalInsetsFrameProvider(provider1, owner);
container.addLocalInsetsFrameProvider(provider2); container.addLocalInsetsFrameProvider(provider2, owner);
mDisplayContent.getInsetsStateController().onPostLayout(); mDisplayContent.getInsetsStateController().onPostLayout();
rootTask.removeLocalInsetsFrameProvider(provider1); rootTask.removeLocalInsetsFrameProvider(provider1, owner);
mDisplayContent.getInsetsStateController().onPostLayout(); mDisplayContent.getInsetsStateController().onPostLayout();
activity0.forAllWindows(window -> { activity0.forAllWindows(window -> {
@@ -1593,6 +1607,67 @@ public class WindowContainerTests extends WindowTestsBase {
}, true); }, 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 */ /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer; private final int mLayer;
@@ -1797,4 +1872,82 @@ public class WindowContainerTests extends WindowTestsBase {
mIsVisibleRequested = isVisibleRequested; 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;
}
}
} }