summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/display/DisplayManager.java64
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java85
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl1
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java127
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java61
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java218
6 files changed, 488 insertions, 68 deletions
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 2c3e7f18a3ab..6dd67447c321 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -20,6 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -376,6 +377,43 @@ public final class DisplayManager {
@TestApi
public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2;
+ /**
+ * @hide
+ */
+ @LongDef(flag = true, prefix = {"EVENT_FLAG_"}, value = {
+ EVENT_FLAG_DISPLAY_ADDED,
+ EVENT_FLAG_DISPLAY_CHANGED,
+ EVENT_FLAG_DISPLAY_REMOVED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventsMask {}
+
+ /**
+ * Event type for when a new display is added.
+ *
+ * @see #registerDisplayListener(DisplayListener, Handler, long)
+ *
+ * @hide
+ */
+ public static final long EVENT_FLAG_DISPLAY_ADDED = 1L << 0;
+
+ /**
+ * Event type for when a display is removed.
+ *
+ * @see #registerDisplayListener(DisplayListener, Handler, long)
+ *
+ * @hide
+ */
+ public static final long EVENT_FLAG_DISPLAY_REMOVED = 1L << 1;
+
+ /**
+ * Event type for when a display is changed.
+ *
+ * @see #registerDisplayListener(DisplayListener, Handler, long)
+ *
+ * @hide
+ */
+ public static final long EVENT_FLAG_DISPLAY_CHANGED = 1L << 2;
/** @hide */
public DisplayManager(Context context) {
@@ -486,7 +524,7 @@ public final class DisplayManager {
}
/**
- * Registers an display listener to receive notifications about when
+ * Registers a display listener to receive notifications about when
* displays are added, removed or changed.
*
* @param listener The listener to register.
@@ -496,7 +534,29 @@ public final class DisplayManager {
* @see #unregisterDisplayListener
*/
public void registerDisplayListener(DisplayListener listener, Handler handler) {
- mGlobal.registerDisplayListener(listener, handler);
+ registerDisplayListener(listener, handler, EVENT_FLAG_DISPLAY_ADDED
+ | EVENT_FLAG_DISPLAY_CHANGED | EVENT_FLAG_DISPLAY_REMOVED);
+ }
+
+ /**
+ * Registers a display listener to receive notifications about given display event types.
+ *
+ * @param listener The listener to register.
+ * @param handler The handler on which the listener should be invoked, or null
+ * if the listener should be invoked on the calling thread's looper.
+ * @param eventsMask A bitmask of the event types for which this listener is subscribed.
+ *
+ * @see #EVENT_FLAG_DISPLAY_ADDED
+ * @see #EVENT_FLAG_DISPLAY_CHANGED
+ * @see #EVENT_FLAG_DISPLAY_REMOVED
+ * @see #registerDisplayListener(DisplayListener, Handler)
+ * @see #unregisterDisplayListener
+ *
+ * @hide
+ */
+ public void registerDisplayListener(@NonNull DisplayListener listener,
+ @Nullable Handler handler, @EventsMask long eventsMask) {
+ mGlobal.registerDisplayListener(listener, handler, eventsMask);
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 60fe5825d6a1..fd0431c5bc3f 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -16,6 +16,9 @@
package android.hardware.display;
+import static android.hardware.display.DisplayManager.EventsMask;
+
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PropertyInvalidatedCache;
@@ -42,6 +45,10 @@ import android.view.DisplayAdjustments;
import android.view.DisplayInfo;
import android.view.Surface;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -66,6 +73,14 @@ public final class DisplayManagerGlobal {
// orientation change before the display info cache has actually been invalidated.
private static final boolean USE_CACHE = false;
+ @IntDef(prefix = {"SWITCHING_TYPE_"}, value = {
+ EVENT_DISPLAY_ADDED,
+ EVENT_DISPLAY_CHANGED,
+ EVENT_DISPLAY_REMOVED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DisplayEvent {}
+
public static final int EVENT_DISPLAY_ADDED = 1;
public static final int EVENT_DISPLAY_CHANGED = 2;
public static final int EVENT_DISPLAY_REMOVED = 3;
@@ -81,16 +96,17 @@ public final class DisplayManagerGlobal {
private final IDisplayManager mDm;
private DisplayManagerCallback mCallback;
- private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
- new ArrayList<DisplayListenerDelegate>();
+ private @EventsMask long mRegisteredEventsMask = 0;
+ private final ArrayList<DisplayListenerDelegate> mDisplayListeners = new ArrayList<>();
- private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>();
+ private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<>();
private final ColorSpace mWideColorSpace;
private int[] mDisplayIdCache;
private int mWifiDisplayScanNestCount;
- private DisplayManagerGlobal(IDisplayManager dm) {
+ @VisibleForTesting
+ public DisplayManagerGlobal(IDisplayManager dm) {
mDm = dm;
try {
mWideColorSpace =
@@ -274,18 +290,25 @@ public final class DisplayManagerGlobal {
* If that is still null, a runtime exception will be thrown.
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
- @Nullable Handler handler) {
+ @Nullable Handler handler, @EventsMask long eventsMask) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
+ if (eventsMask == 0) {
+ throw new IllegalArgumentException("The set of events to listen to must not be empty.");
+ }
+
synchronized (mLock) {
int index = findDisplayListenerLocked(listener);
if (index < 0) {
Looper looper = getLooperForHandler(handler);
- mDisplayListeners.add(new DisplayListenerDelegate(listener, looper));
+ mDisplayListeners.add(new DisplayListenerDelegate(listener, looper, eventsMask));
registerCallbackIfNeededLocked();
+ } else {
+ mDisplayListeners.get(index).setEventsMask(eventsMask);
}
+ updateCallbackIfNeededLocked();
}
}
@@ -300,6 +323,7 @@ public final class DisplayManagerGlobal {
DisplayListenerDelegate d = mDisplayListeners.get(index);
d.clearEvents();
mDisplayListeners.remove(index);
+ updateCallbackIfNeededLocked();
}
}
}
@@ -325,18 +349,36 @@ public final class DisplayManagerGlobal {
return -1;
}
+ @EventsMask
+ private int calculateEventsMaskLocked() {
+ int mask = 0;
+ final int numListeners = mDisplayListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ mask |= mDisplayListeners.get(i).mEventsMask;
+ }
+ return mask;
+ }
+
private void registerCallbackIfNeededLocked() {
if (mCallback == null) {
mCallback = new DisplayManagerCallback();
+ updateCallbackIfNeededLocked();
+ }
+ }
+
+ private void updateCallbackIfNeededLocked() {
+ int mask = calculateEventsMaskLocked();
+ if (mask != mRegisteredEventsMask) {
try {
- mDm.registerCallback(mCallback);
+ mDm.registerCallbackWithEventMask(mCallback, mask);
+ mRegisteredEventsMask = mask;
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
- private void handleDisplayEvent(int displayId, int event) {
+ private void handleDisplayEvent(int displayId, @DisplayEvent int event) {
synchronized (mLock) {
if (USE_CACHE) {
mDisplayInfoCache.remove(displayId);
@@ -754,7 +796,7 @@ public final class DisplayManagerGlobal {
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
- public void onDisplayEvent(int displayId, int event) {
+ public void onDisplayEvent(int displayId, @DisplayEvent int event) {
if (DEBUG) {
Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
}
@@ -764,13 +806,16 @@ public final class DisplayManagerGlobal {
private static final class DisplayListenerDelegate extends Handler {
public final DisplayListener mListener;
+ public long mEventsMask;
- DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper) {
+ DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper,
+ @EventsMask long eventsMask) {
super(looper, null, true /*async*/);
mListener = listener;
+ mEventsMask = eventsMask;
}
- public void sendDisplayEvent(int displayId, int event) {
+ public void sendDisplayEvent(int displayId, @DisplayEvent int event) {
Message msg = obtainMessage(event, displayId, 0);
sendMessage(msg);
}
@@ -779,17 +824,27 @@ public final class DisplayManagerGlobal {
removeCallbacksAndMessages(null);
}
+ public synchronized void setEventsMask(@EventsMask long newEventsMask) {
+ mEventsMask = newEventsMask;
+ }
+
@Override
- public void handleMessage(Message msg) {
+ public synchronized void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_DISPLAY_ADDED:
- mListener.onDisplayAdded(msg.arg1);
+ if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
+ mListener.onDisplayAdded(msg.arg1);
+ }
break;
case EVENT_DISPLAY_CHANGED:
- mListener.onDisplayChanged(msg.arg1);
+ if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
+ mListener.onDisplayChanged(msg.arg1);
+ }
break;
case EVENT_DISPLAY_REMOVED:
- mListener.onDisplayRemoved(msg.arg1);
+ if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) {
+ mListener.onDisplayRemoved(msg.arg1);
+ }
break;
}
}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index ff8a7208a9f0..dee91445c224 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -38,6 +38,7 @@ interface IDisplayManager {
boolean isUidPresentOnDisplay(int uid, int displayId);
void registerCallback(in IDisplayManagerCallback callback);
+ void registerCallbackWithEventMask(in IDisplayManagerCallback callback, long eventsMask);
// Requires CONFIGURE_WIFI_DISPLAY permission.
// The process must have previously registered a callback.
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
new file mode 100644
index 000000000000..dfc9013e3c05
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DisplayManagerGlobalTest {
+
+ private static final long ALL_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
+
+ @Mock
+ private IDisplayManager mDisplayManager;
+
+ @Mock
+ private DisplayManager.DisplayListener mListener;
+
+ @Captor
+ private ArgumentCaptor<IDisplayManagerCallback> mCallbackCaptor;
+
+ private Context mContext;
+ private DisplayManagerGlobal mDisplayManagerGlobal;
+ private Handler mHandler;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ Mockito.when(mDisplayManager.getPreferredWideGamutColorSpaceId()).thenReturn(0);
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mHandler = mContext.getMainThreadHandler();
+ mDisplayManagerGlobal = new DisplayManagerGlobal(mDisplayManager);
+ }
+
+ @Test
+ public void testDisplayListenerIsCalled_WhenDisplayEventOccurs() throws RemoteException {
+ mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler, ALL_DISPLAY_EVENTS);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
+ IDisplayManagerCallback callback = mCallbackCaptor.getValue();
+
+ int displayId = 1;
+ callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
+ waitForHandler();
+ Mockito.verify(mListener).onDisplayAdded(eq(displayId));
+ Mockito.verifyNoMoreInteractions(mListener);
+
+ Mockito.reset(mListener);
+ callback.onDisplayEvent(1, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ waitForHandler();
+ Mockito.verify(mListener).onDisplayChanged(eq(displayId));
+ Mockito.verifyNoMoreInteractions(mListener);
+
+ Mockito.reset(mListener);
+ callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+ waitForHandler();
+ Mockito.verify(mListener).onDisplayRemoved(eq(displayId));
+ Mockito.verifyNoMoreInteractions(mListener);
+ }
+
+ @Test
+ public void testDisplayListenerIsNotCalled_WhenClientIsNotSubscribed() throws RemoteException {
+ // First we subscribe to all events in order to test that the subsequent calls to
+ // registerDisplayListener will update the event mask.
+ mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler, ALL_DISPLAY_EVENTS);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
+ IDisplayManagerCallback callback = mCallbackCaptor.getValue();
+
+ int displayId = 1;
+ mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
+ ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_ADDED);
+ callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
+ waitForHandler();
+ Mockito.verifyZeroInteractions(mListener);
+
+ mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
+ ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
+ callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ waitForHandler();
+ Mockito.verifyZeroInteractions(mListener);
+
+ mDisplayManagerGlobal.registerDisplayListener(mListener, mHandler,
+ ALL_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+ callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+ waitForHandler();
+ Mockito.verifyZeroInteractions(mListener);
+ }
+
+ private void waitForHandler() {
+ mHandler.runWithScissors(() -> { }, 0);
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 82ca820ec4d7..f8a913aeb7cd 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT;
import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.hardware.display.DisplayManager.EventsMask;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
@@ -28,6 +29,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLI
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+import static android.hardware.display.DisplayManagerGlobal.DisplayEvent;
import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL;
@@ -125,6 +127,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
/**
@@ -820,14 +823,16 @@ public final class DisplayManagerService extends SystemService {
}
private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid,
- int callingUid) {
+ int callingUid, @EventsMask long eventsMask) {
synchronized (mSyncRoot) {
- if (mCallbacks.get(callingPid) != null) {
- throw new SecurityException("The calling process has already "
- + "registered an IDisplayManagerCallback.");
+ CallbackRecord record = mCallbacks.get(callingPid);
+
+ if (record != null) {
+ record.updateEventsMask(eventsMask);
+ return;
}
- CallbackRecord record = new CallbackRecord(callingPid, callingUid, callback);
+ record = new CallbackRecord(callingPid, callingUid, callback, eventsMask);
try {
IBinder binder = callback.asBinder();
binder.linkToDeath(record, 0);
@@ -1695,7 +1700,7 @@ public final class DisplayManagerService extends SystemService {
}
}
- private void sendDisplayEventLocked(int displayId, int event) {
+ private void sendDisplayEventLocked(int displayId, @DisplayEvent int event) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
mHandler.sendMessage(msg);
}
@@ -1724,7 +1729,8 @@ public final class DisplayManagerService extends SystemService {
// Runs on Handler thread.
// Delivers display event notifications to callbacks.
- private void deliverDisplayEvent(int displayId, ArraySet<Integer> uids, int event) {
+ private void deliverDisplayEvent(int displayId, ArraySet<Integer> uids,
+ @DisplayEvent int event) {
if (DEBUG) {
Slog.d(TAG, "Delivering display event: displayId="
+ displayId + ", event=" + event);
@@ -2059,13 +2065,20 @@ public final class DisplayManagerService extends SystemService {
public final int mPid;
public final int mUid;
private final IDisplayManagerCallback mCallback;
+ private @EventsMask AtomicLong mEventsMask;
public boolean mWifiDisplayScanRequested;
- CallbackRecord(int pid, int uid, IDisplayManagerCallback callback) {
+ CallbackRecord(int pid, int uid, IDisplayManagerCallback callback,
+ @EventsMask long eventsMask) {
mPid = pid;
mUid = uid;
mCallback = callback;
+ mEventsMask = new AtomicLong(eventsMask);
+ }
+
+ public void updateEventsMask(@EventsMask long eventsMask) {
+ mEventsMask.set(eventsMask);
}
@Override
@@ -2076,7 +2089,11 @@ public final class DisplayManagerService extends SystemService {
onCallbackDied(this);
}
- public void notifyDisplayEventAsync(int displayId, int event) {
+ public void notifyDisplayEventAsync(int displayId, @DisplayEvent int event) {
+ if (!shouldSendEvent(event)) {
+ return;
+ }
+
try {
mCallback.onDisplayEvent(displayId, event);
} catch (RemoteException ex) {
@@ -2085,6 +2102,22 @@ public final class DisplayManagerService extends SystemService {
binderDied();
}
}
+
+ private boolean shouldSendEvent(@DisplayEvent int event) {
+ final long mask = mEventsMask.get();
+ switch (event) {
+ case DisplayManagerGlobal.EVENT_DISPLAY_ADDED:
+ return (mask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0;
+ case DisplayManagerGlobal.EVENT_DISPLAY_CHANGED:
+ return (mask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0;
+ case DisplayManagerGlobal.EVENT_DISPLAY_REMOVED:
+ return (mask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0;
+ default:
+ // This should never happen.
+ Slog.e(TAG, "Unknown display event " + event);
+ return true;
+ }
+ }
}
@VisibleForTesting
@@ -2149,6 +2182,14 @@ public final class DisplayManagerService extends SystemService {
@Override // Binder call
public void registerCallback(IDisplayManagerCallback callback) {
+ registerCallbackWithEventMask(callback, DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+ }
+
+ @Override // Binder call
+ public void registerCallbackWithEventMask(IDisplayManagerCallback callback,
+ @EventsMask long eventsMask) {
if (callback == null) {
throw new IllegalArgumentException("listener must not be null");
}
@@ -2157,7 +2198,7 @@ public final class DisplayManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- registerCallbackInternal(callback, callingPid, callingUid);
+ registerCallbackInternal(callback, callingPid, callingUid, eventsMask);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index bc86d1d39b1c..429517251f67 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -93,7 +93,9 @@ public class DisplayManagerServiceTest {
private static final long SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS = 10;
private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display";
private static final String PACKAGE_NAME = "com.android.frameworks.servicestests";
-
+ private static final long ALL_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
@@ -355,29 +357,13 @@ public class DisplayManagerServiceTest {
// Find the display id of the added FakeDisplayDevice
DisplayManagerService.BinderService bs = displayManager.new BinderService();
- final int[] displayIds = bs.getDisplayIds();
- assertTrue(displayIds.length > 0);
- int displayId = Display.INVALID_DISPLAY;
- for (int i = 0; i < displayIds.length; i++) {
- DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayIds[i]);
- if (displayDeviceInfo.equals(ddi)) {
- displayId = displayIds[i];
- break;
- }
- }
- assertFalse(displayId == Display.INVALID_DISPLAY);
-
+ int displayId = getDisplayIdForDisplayDevice(displayManager, bs, displayDevice);
// Setup override DisplayInfo
DisplayInfo overrideInfo = bs.getDisplayInfo(displayId);
displayManager.setDisplayInfoOverrideFromWindowManagerInternal(displayId, overrideInfo);
- Handler handler = displayManager.getDisplayHandler();
- handler.runWithScissors(() -> {
- }, 0 /* now */);
-
- // register display listener callback
- FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(displayId);
- bs.registerCallback(callback);
+ FakeDisplayManagerCallback callback = registerDisplayListenerCallback(
+ displayManager, bs, displayDevice);
// Simulate DisplayDevice change
DisplayDeviceInfo displayDeviceInfo2 = new DisplayDeviceInfo();
@@ -387,9 +373,9 @@ public class DisplayManagerServiceTest {
displayManager.getDisplayDeviceRepository()
.onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED);
- handler.runWithScissors(() -> {
- }, 0 /* now */);
- assertTrue(callback.mCalled);
+ Handler handler = displayManager.getDisplayHandler();
+ waitForIdleHandler(handler);
+ assertTrue(callback.mDisplayChangedCalled);
}
/**
@@ -400,7 +386,7 @@ public class DisplayManagerServiceTest {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
Handler handler = displayManager.getDisplayHandler();
- handler.runWithScissors(() -> {}, 0 /* now */);
+ waitForIdleHandler(handler);
try {
displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
@@ -616,7 +602,7 @@ public class DisplayManagerServiceTest {
}
/**
- * Tests that there should be a display change notification if the frame rate overrides
+ * Tests that there is a display change notification if the frame rate override
* list is updated.
*/
@Test
@@ -637,7 +623,7 @@ public class DisplayManagerServiceTest {
new DisplayEventReceiver.FrameRateOverride[]{
new DisplayEventReceiver.FrameRateOverride(myUid, 30f),
});
- assertTrue(callback.mCalled);
+ assertTrue(callback.mDisplayChangedCalled);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
@@ -645,7 +631,7 @@ public class DisplayManagerServiceTest {
new DisplayEventReceiver.FrameRateOverride(myUid, 30f),
new DisplayEventReceiver.FrameRateOverride(1234, 30f),
});
- assertFalse(callback.mCalled);
+ assertFalse(callback.mDisplayChangedCalled);
updateFrameRateOverride(displayManager, displayDevice,
new DisplayEventReceiver.FrameRateOverride[]{
@@ -653,7 +639,7 @@ public class DisplayManagerServiceTest {
new DisplayEventReceiver.FrameRateOverride(1234, 30f),
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
- assertTrue(callback.mCalled);
+ assertTrue(callback.mDisplayChangedCalled);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
@@ -661,14 +647,14 @@ public class DisplayManagerServiceTest {
new DisplayEventReceiver.FrameRateOverride(1234, 30f),
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
- assertTrue(callback.mCalled);
+ assertTrue(callback.mDisplayChangedCalled);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
new DisplayEventReceiver.FrameRateOverride[]{
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
- assertFalse(callback.mCalled);
+ assertFalse(callback.mDisplayChangedCalled);
}
/**
@@ -760,6 +746,136 @@ public class DisplayManagerServiceTest {
/*compatChangeEnabled*/ true);
}
+ /**
+ * Tests that EVENT_DISPLAY_ADDED is sent when a display is added.
+ */
+ @Test
+ public void testShouldNotifyDisplayAdded_WhenNewDisplayDeviceIsAdded() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+
+ Handler handler = displayManager.getDisplayHandler();
+ waitForIdleHandler(handler);
+
+ // register display listener callback
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ displayManagerBinderService.registerCallbackWithEventMask(callback, ALL_DISPLAY_EVENTS);
+
+ waitForIdleHandler(handler);
+
+ createFakeDisplayDevice(displayManager, new float[]{60f});
+
+ waitForIdleHandler(handler);
+
+ assertFalse(callback.mDisplayChangedCalled);
+ assertFalse(callback.mDisplayRemovedCalled);
+ assertTrue(callback.mDisplayAddedCalled);
+ }
+
+ /**
+ * Tests that EVENT_DISPLAY_ADDED is not sent when a display is added and the
+ * client has a callback which is not subscribed to this event type.
+ */
+ @Test
+ public void testShouldNotNotifyDisplayAdded_WhenClientIsNotSubscribed() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+
+ Handler handler = displayManager.getDisplayHandler();
+ waitForIdleHandler(handler);
+
+ // register display listener callback
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ long allEventsExceptDisplayAdded = ALL_DISPLAY_EVENTS
+ & ~DisplayManager.EVENT_FLAG_DISPLAY_ADDED;
+ displayManagerBinderService.registerCallbackWithEventMask(callback,
+ allEventsExceptDisplayAdded);
+
+ waitForIdleHandler(handler);
+
+ createFakeDisplayDevice(displayManager, new float[]{60f});
+
+ waitForIdleHandler(handler);
+
+ assertFalse(callback.mDisplayChangedCalled);
+ assertFalse(callback.mDisplayRemovedCalled);
+ assertFalse(callback.mDisplayAddedCalled);
+ }
+
+ /**
+ * Tests that EVENT_DISPLAY_REMOVED is sent when a display is removed.
+ */
+ @Test
+ public void testShouldNotifyDisplayRemoved_WhenDisplayDeviceIsRemoved() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+
+ Handler handler = displayManager.getDisplayHandler();
+ waitForIdleHandler(handler);
+
+ FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager,
+ new float[]{60f});
+
+ waitForIdleHandler(handler);
+
+ FakeDisplayManagerCallback callback = registerDisplayListenerCallback(
+ displayManager, displayManagerBinderService, displayDevice);
+
+ waitForIdleHandler(handler);
+
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
+
+ waitForIdleHandler(handler);
+
+ assertFalse(callback.mDisplayChangedCalled);
+ assertTrue(callback.mDisplayRemovedCalled);
+ assertFalse(callback.mDisplayAddedCalled);
+ }
+
+ /**
+ * Tests that EVENT_DISPLAY_REMOVED is not sent when a display is added and the
+ * client has a callback which is not subscribed to this event type.
+ */
+ @Test
+ public void testShouldNotNotifyDisplayRemoved_WhenClientIsNotSubscribed() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+
+ Handler handler = displayManager.getDisplayHandler();
+ waitForIdleHandler(handler);
+
+ FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager,
+ new float[]{60f});
+
+ waitForIdleHandler(handler);
+
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ long allEventsExceptDisplayRemoved = ALL_DISPLAY_EVENTS
+ & ~DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
+ displayManagerBinderService.registerCallbackWithEventMask(callback,
+ allEventsExceptDisplayRemoved);
+
+ waitForIdleHandler(handler);
+
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
+
+ waitForIdleHandler(handler);
+
+ assertFalse(callback.mDisplayChangedCalled);
+ assertFalse(callback.mDisplayRemovedCalled);
+ assertFalse(callback.mDisplayAddedCalled);
+ }
+
private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled)
throws Exception {
DisplayManagerService displayManager =
@@ -879,8 +995,7 @@ public class DisplayManagerServiceTest {
displayManager.getDisplayDeviceRepository()
.onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED);
Handler handler = displayManager.getDisplayHandler();
- handler.runWithScissors(() -> {
- }, 0 /* now */);
+ waitForIdleHandler(handler);
}
private void updateFrameRateOverride(DisplayManagerService displayManager,
@@ -906,18 +1021,15 @@ public class DisplayManagerServiceTest {
DisplayManagerService.BinderService displayManagerBinderService,
FakeDisplayDevice displayDevice) {
// Find the display id of the added FakeDisplayDevice
- DisplayDeviceInfo displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
-
int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
displayDevice);
Handler handler = displayManager.getDisplayHandler();
- handler.runWithScissors(() -> {
- }, 0 /* now */);
+ waitForIdleHandler(handler);
// register display listener callback
FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(displayId);
- displayManagerBinderService.registerCallback(callback);
+ displayManagerBinderService.registerCallbackWithEventMask(callback, ALL_DISPLAY_EVENTS);
return callback;
}
@@ -951,6 +1063,10 @@ public class DisplayManagerServiceTest {
// Would prefer to call displayManager.onStart() directly here but it performs binderService
// registration which triggers security exceptions when running from a test.
handler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS);
+ waitForIdleHandler(handler);
+ }
+
+ private void waitForIdleHandler(Handler handler) {
waitForIdleHandler(handler, Duration.ofSeconds(1));
}
@@ -971,21 +1087,41 @@ public class DisplayManagerServiceTest {
private class FakeDisplayManagerCallback extends IDisplayManagerCallback.Stub {
int mDisplayId;
- boolean mCalled = false;
+ boolean mDisplayAddedCalled = false;
+ boolean mDisplayChangedCalled = false;
+ boolean mDisplayRemovedCalled = false;
FakeDisplayManagerCallback(int displayId) {
mDisplayId = displayId;
}
+ FakeDisplayManagerCallback() {
+ mDisplayId = -1;
+ }
+
@Override
public void onDisplayEvent(int displayId, int event) {
- if (displayId == mDisplayId && event == DisplayManagerGlobal.EVENT_DISPLAY_CHANGED) {
- mCalled = true;
+ if (mDisplayId != -1 && displayId != mDisplayId) {
+ return;
+ }
+
+ if (event == DisplayManagerGlobal.EVENT_DISPLAY_ADDED) {
+ mDisplayAddedCalled = true;
+ }
+
+ if (event == DisplayManagerGlobal.EVENT_DISPLAY_CHANGED) {
+ mDisplayChangedCalled = true;
+ }
+
+ if (event == DisplayManagerGlobal.EVENT_DISPLAY_REMOVED) {
+ mDisplayRemovedCalled = true;
}
}
public void clear() {
- mCalled = false;
+ mDisplayAddedCalled = false;
+ mDisplayChangedCalled = false;
+ mDisplayRemovedCalled = false;
}
}