diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index fc628cfdced27..000bafe1d650d 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -44,6 +44,7 @@ import android.view.Display; import android.window.DisplayWindowPolicyController; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.BlockedAppStreamingActivity; import java.util.List; @@ -112,7 +113,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController final ArraySet mRunningUids = new ArraySet<>(); @Nullable private final ActivityListener mActivityListener; private final Handler mHandler = new Handler(Looper.getMainLooper()); - private final ArraySet mRunningAppsChangedListener = + @NonNull + @GuardedBy("mGenericWindowPolicyControllerLock") + private final ArraySet mRunningAppsChangedListeners = new ArraySet<>(); @Nullable private final @AssociationRequest.DeviceProfile String mDeviceProfile; @@ -178,12 +181,16 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController /** Register a listener for running applications changes. */ public void registerRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) { - mRunningAppsChangedListener.add(listener); + synchronized (mGenericWindowPolicyControllerLock) { + mRunningAppsChangedListeners.add(listener); + } } /** Unregister a listener for running applications changes. */ public void unregisterRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) { - mRunningAppsChangedListener.remove(listener); + synchronized (mGenericWindowPolicyControllerLock) { + mRunningAppsChangedListeners.remove(listener); + } } @Override @@ -283,12 +290,16 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController // Post callback on the main thread so it doesn't block activity launching mHandler.post(() -> mActivityListener.onDisplayEmpty(mDisplayId)); } - } - mHandler.post(() -> { - for (RunningAppsChangedListener listener : mRunningAppsChangedListener) { - listener.onRunningAppsChanged(runningUids); + if (!mRunningAppsChangedListeners.isEmpty()) { + final ArraySet listeners = + new ArraySet<>(mRunningAppsChangedListeners); + mHandler.post(() -> { + for (RunningAppsChangedListener listener : listeners) { + listener.onRunningAppsChanged(runningUids); + } + }); } - }); + } } @Override @@ -354,4 +365,11 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController } return true; } + + @VisibleForTesting + int getRunningAppsChangedListenersSizeForTesting() { + synchronized (mGenericWindowPolicyControllerLock) { + return mRunningAppsChangedListeners.size(); + } + } } 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 02bbe658f9b27..5fda3d6b36ab5 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 @@ -891,4 +891,34 @@ public class VirtualDeviceManagerServiceTest { verify(mContext).startActivityAsUser(argThat(intent -> intent.filterEquals(blockedAppIntent)), any(), any()); } + + @Test + public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() { + ArraySet uids = new ArraySet<>(Arrays.asList(UID_1, UID_2)); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); + GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( + DISPLAY_ID); + + gwpc.onRunningAppsChanged(uids); + mDeviceImpl.onRunningAppsChanged(uids); + + assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(1); + verify(mRunningAppsChangedCallback).accept(new ArraySet<>(Arrays.asList(UID_1, UID_2))); + } + + @Test + public void noRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() { + ArraySet uids = new ArraySet<>(Arrays.asList(UID_1, UID_2)); + mDeviceImpl.onVirtualDisplayCreatedLocked( + mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); + GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( + DISPLAY_ID); + mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID); + + // This call should not throw any exceptions. + gwpc.onRunningAppsChanged(uids); + + assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0); + } }