diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 35e9060b58d42..b25518875cf02 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -56,6 +56,7 @@ import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -208,6 +209,10 @@ public class VirtualDeviceManagerService extends SystemService { } @VisibleForTesting + VirtualDeviceManagerInternal getLocalServiceInstance() { + return mLocalService; + } + class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements VirtualDeviceImpl.PendingTrampolineCallback { @@ -308,10 +313,11 @@ public class VirtualDeviceManagerService extends SystemService { final long tokenTwo = Binder.clearCallingIdentity(); try { virtualDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, displayId); - return displayId; } finally { Binder.restoreCallingIdentity(tokenTwo); } + mLocalService.onVirtualDisplayCreated(displayId); + return displayId; } @Nullable @@ -378,6 +384,10 @@ public class VirtualDeviceManagerService extends SystemService { } private final class LocalService extends VirtualDeviceManagerInternal { + @GuardedBy("mVirtualDeviceManagerLock") + private final ArrayList + mVirtualDisplayListeners = new ArrayList<>(); + @Override public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) { synchronized (mVirtualDeviceManagerLock) { @@ -385,11 +395,31 @@ public class VirtualDeviceManagerService extends SystemService { } } + @Override + public void onVirtualDisplayCreated(int displayId) { + final VirtualDisplayListener[] listeners; + synchronized (mVirtualDeviceManagerLock) { + listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]); + } + mHandler.post(() -> { + for (VirtualDisplayListener listener : listeners) { + listener.onVirtualDisplayCreated(displayId); + } + }); + } + @Override public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) { + final VirtualDisplayListener[] listeners; synchronized (mVirtualDeviceManagerLock) { ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId); + listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]); } + mHandler.post(() -> { + for (VirtualDisplayListener listener : listeners) { + listener.onVirtualDisplayRemoved(displayId); + } + }); } @Override @@ -435,6 +465,22 @@ public class VirtualDeviceManagerService extends SystemService { } return false; } + + @Override + public void registerVirtualDisplayListener( + @NonNull VirtualDisplayListener listener) { + synchronized (mVirtualDeviceManagerLock) { + mVirtualDisplayListeners.add(listener); + } + } + + @Override + public void unregisterVirtualDisplayListener( + @NonNull VirtualDisplayListener listener) { + synchronized (mVirtualDeviceManagerLock) { + mVirtualDisplayListeners.remove(listener); + } + } } private static final class PendingTrampolineMap { diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java index f2b4d42c87585..230bfc50d1864 100644 --- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java +++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java @@ -16,6 +16,7 @@ package com.android.server.companion.virtual; +import android.annotation.NonNull; import android.companion.virtual.IVirtualDevice; /** @@ -24,16 +25,41 @@ import android.companion.virtual.IVirtualDevice; */ public abstract class VirtualDeviceManagerInternal { + /** Interface to listen to the creation and destruction of virtual displays. */ + public interface VirtualDisplayListener { + /** Notifies that a virtual display was created. */ + void onVirtualDisplayCreated(int displayId); + + /** Notifies that a virtual display was removed. */ + void onVirtualDisplayRemoved(int displayId); + } + + /** Register a listener for the creation and destruction of virtual displays. */ + public abstract void registerVirtualDisplayListener( + @NonNull VirtualDisplayListener listener); + + /** Unregister a listener for the creation and destruction of virtual displays. */ + public abstract void unregisterVirtualDisplayListener( + @NonNull VirtualDisplayListener listener); + + /** * Validate the virtual device. */ public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice); /** - * Notify a virtual display is removed. + * Notifies that a virtual display is created. + * + * @param displayId The display id of the created virtual display. + */ + public abstract void onVirtualDisplayCreated(int displayId); + + /** + * Notifies that a virtual display is removed. * * @param virtualDevice The virtual device where the virtual display located. - * @param displayId The display id of the removed virtual display. + * @param displayId The display id of the removed virtual display. */ public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index c80547ce61c01..966df4f927261 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -124,6 +124,8 @@ public class VirtualDeviceManagerServiceTest { private VirtualDeviceImpl mDeviceImpl; private InputController mInputController; private AssociationInfo mAssociationInfo; + private VirtualDeviceManagerService mVdms; + private VirtualDeviceManagerInternal mLocalService; @Mock private InputController.NativeWrapper mNativeWrapperMock; @Mock @@ -139,6 +141,8 @@ public class VirtualDeviceManagerServiceTest { @Mock private Consumer> mRunningAppsChangedCallback; @Mock + private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener; + @Mock IPowerManager mIPowerManagerMock; @Mock IThermalService mIThermalServiceMock; @@ -215,6 +219,9 @@ public class VirtualDeviceManagerServiceTest { mAssociationInfo = new AssociationInfo(1, 0, null, MacAddress.BROADCAST_ADDRESS, "", null, true, false, false, 0, 0); + mVdms = new VirtualDeviceManagerService(mContext); + mLocalService = mVdms.getLocalServiceInstance(); + VirtualDeviceParams params = new VirtualDeviceParams .Builder() .setBlockedActivities(getBlockedActivities()) @@ -234,6 +241,28 @@ public class VirtualDeviceManagerServiceTest { mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID); } + @Test + public void onVirtualDisplayCreatedLocked_listenersNotified() throws RemoteException { + mLocalService.registerVirtualDisplayListener(mDisplayListener); + + mLocalService.onVirtualDisplayCreated(DISPLAY_ID); + TestableLooper.get(this).processAllMessages(); + + verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID); + } + + @Test + public void onVirtualDisplayRemovedLocked_listenersNotified() throws RemoteException { + mLocalService.registerVirtualDisplayListener(mDisplayListener); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); + + mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID); + TestableLooper.get(this).processAllMessages(); + + verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID); + } + @Test public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException { verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),