Loading core/java/android/hardware/display/DisplayManager.java +62 −2 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import android.Manifest; import android.Manifest; import android.annotation.IntDef; import android.annotation.IntDef; import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.RequiresPermission; Loading Loading @@ -376,6 +377,43 @@ public final class DisplayManager { @TestApi @TestApi public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; 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 */ /** @hide */ public DisplayManager(Context context) { public DisplayManager(Context context) { Loading Loading @@ -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. * displays are added, removed or changed. * * * @param listener The listener to register. * @param listener The listener to register. Loading @@ -496,7 +534,29 @@ public final class DisplayManager { * @see #unregisterDisplayListener * @see #unregisterDisplayListener */ */ public void registerDisplayListener(DisplayListener listener, Handler handler) { 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); } } /** /** Loading core/java/android/hardware/display/DisplayManagerGlobal.java +70 −15 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,9 @@ package android.hardware.display; package android.hardware.display; import static android.hardware.display.DisplayManager.EventsMask; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.app.PropertyInvalidatedCache; import android.app.PropertyInvalidatedCache; Loading @@ -42,6 +45,10 @@ import android.view.DisplayAdjustments; import android.view.DisplayInfo; import android.view.DisplayInfo; import android.view.Surface; 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.ArrayList; import java.util.Collections; import java.util.Collections; import java.util.List; import java.util.List; Loading @@ -66,6 +73,14 @@ public final class DisplayManagerGlobal { // orientation change before the display info cache has actually been invalidated. // orientation change before the display info cache has actually been invalidated. private static final boolean USE_CACHE = false; 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_ADDED = 1; public static final int EVENT_DISPLAY_CHANGED = 2; public static final int EVENT_DISPLAY_CHANGED = 2; public static final int EVENT_DISPLAY_REMOVED = 3; public static final int EVENT_DISPLAY_REMOVED = 3; Loading @@ -81,16 +96,17 @@ public final class DisplayManagerGlobal { private final IDisplayManager mDm; private final IDisplayManager mDm; private DisplayManagerCallback mCallback; private DisplayManagerCallback mCallback; private final ArrayList<DisplayListenerDelegate> mDisplayListeners = private @EventsMask long mRegisteredEventsMask = 0; new ArrayList<DisplayListenerDelegate>(); 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 final ColorSpace mWideColorSpace; private int[] mDisplayIdCache; private int[] mDisplayIdCache; private int mWifiDisplayScanNestCount; private int mWifiDisplayScanNestCount; private DisplayManagerGlobal(IDisplayManager dm) { @VisibleForTesting public DisplayManagerGlobal(IDisplayManager dm) { mDm = dm; mDm = dm; try { try { mWideColorSpace = mWideColorSpace = Loading Loading @@ -274,18 +290,25 @@ public final class DisplayManagerGlobal { * If that is still null, a runtime exception will be thrown. * If that is still null, a runtime exception will be thrown. */ */ public void registerDisplayListener(@NonNull DisplayListener listener, public void registerDisplayListener(@NonNull DisplayListener listener, @Nullable Handler handler) { @Nullable Handler handler, @EventsMask long eventsMask) { if (listener == null) { if (listener == null) { throw new IllegalArgumentException("listener must not be 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) { synchronized (mLock) { int index = findDisplayListenerLocked(listener); int index = findDisplayListenerLocked(listener); if (index < 0) { if (index < 0) { Looper looper = getLooperForHandler(handler); Looper looper = getLooperForHandler(handler); mDisplayListeners.add(new DisplayListenerDelegate(listener, looper)); mDisplayListeners.add(new DisplayListenerDelegate(listener, looper, eventsMask)); registerCallbackIfNeededLocked(); registerCallbackIfNeededLocked(); } else { mDisplayListeners.get(index).setEventsMask(eventsMask); } } updateCallbackIfNeededLocked(); } } } } Loading @@ -300,6 +323,7 @@ public final class DisplayManagerGlobal { DisplayListenerDelegate d = mDisplayListeners.get(index); DisplayListenerDelegate d = mDisplayListeners.get(index); d.clearEvents(); d.clearEvents(); mDisplayListeners.remove(index); mDisplayListeners.remove(index); updateCallbackIfNeededLocked(); } } } } } } Loading @@ -325,18 +349,36 @@ public final class DisplayManagerGlobal { return -1; 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() { private void registerCallbackIfNeededLocked() { if (mCallback == null) { if (mCallback == null) { mCallback = new DisplayManagerCallback(); mCallback = new DisplayManagerCallback(); updateCallbackIfNeededLocked(); } } private void updateCallbackIfNeededLocked() { int mask = calculateEventsMaskLocked(); if (mask != mRegisteredEventsMask) { try { try { mDm.registerCallback(mCallback); mDm.registerCallbackWithEventMask(mCallback, mask); mRegisteredEventsMask = mask; } catch (RemoteException ex) { } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); throw ex.rethrowFromSystemServer(); } } } } } } private void handleDisplayEvent(int displayId, int event) { private void handleDisplayEvent(int displayId, @DisplayEvent int event) { synchronized (mLock) { synchronized (mLock) { if (USE_CACHE) { if (USE_CACHE) { mDisplayInfoCache.remove(displayId); mDisplayInfoCache.remove(displayId); Loading Loading @@ -754,7 +796,7 @@ public final class DisplayManagerGlobal { private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { @Override @Override public void onDisplayEvent(int displayId, int event) { public void onDisplayEvent(int displayId, @DisplayEvent int event) { if (DEBUG) { if (DEBUG) { Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event); Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event); } } Loading @@ -764,13 +806,16 @@ public final class DisplayManagerGlobal { private static final class DisplayListenerDelegate extends Handler { private static final class DisplayListenerDelegate extends Handler { public final DisplayListener mListener; 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*/); super(looper, null, true /*async*/); mListener = listener; 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); Message msg = obtainMessage(event, displayId, 0); sendMessage(msg); sendMessage(msg); } } Loading @@ -779,17 +824,27 @@ public final class DisplayManagerGlobal { removeCallbacksAndMessages(null); removeCallbacksAndMessages(null); } } public synchronized void setEventsMask(@EventsMask long newEventsMask) { mEventsMask = newEventsMask; } @Override @Override public void handleMessage(Message msg) { public synchronized void handleMessage(Message msg) { switch (msg.what) { switch (msg.what) { case EVENT_DISPLAY_ADDED: case EVENT_DISPLAY_ADDED: if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) { mListener.onDisplayAdded(msg.arg1); mListener.onDisplayAdded(msg.arg1); } break; break; case EVENT_DISPLAY_CHANGED: case EVENT_DISPLAY_CHANGED: if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) { mListener.onDisplayChanged(msg.arg1); mListener.onDisplayChanged(msg.arg1); } break; break; case EVENT_DISPLAY_REMOVED: case EVENT_DISPLAY_REMOVED: if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) { mListener.onDisplayRemoved(msg.arg1); mListener.onDisplayRemoved(msg.arg1); } break; break; } } } } Loading core/java/android/hardware/display/IDisplayManager.aidl +1 −0 Original line number Original line Diff line number Diff line Loading @@ -38,6 +38,7 @@ interface IDisplayManager { boolean isUidPresentOnDisplay(int uid, int displayId); boolean isUidPresentOnDisplay(int uid, int displayId); void registerCallback(in IDisplayManagerCallback callback); void registerCallback(in IDisplayManagerCallback callback); void registerCallbackWithEventMask(in IDisplayManagerCallback callback, long eventsMask); // Requires CONFIGURE_WIFI_DISPLAY permission. // Requires CONFIGURE_WIFI_DISPLAY permission. // The process must have previously registered a callback. // The process must have previously registered a callback. Loading core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java 0 → 100644 +127 −0 Original line number Original line Diff line number Diff line /* * 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); } } services/core/java/com/android/server/display/DisplayManagerService.java +51 −10 Original line number Original line Diff line number Diff line Loading @@ -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_SECURE_VIDEO_OUTPUT; import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT; import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; 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_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_CAN_SHOW_WITH_INSECURE_KEYGUARD; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; Loading @@ -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_SECURE; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; 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.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_EXTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL; import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL; Loading Loading @@ -125,6 +127,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Arrays; import java.util.Optional; import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.function.Consumer; /** /** Loading Loading @@ -820,14 +823,16 @@ public final class DisplayManagerService extends SystemService { } } private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid, private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid, int callingUid) { int callingUid, @EventsMask long eventsMask) { synchronized (mSyncRoot) { synchronized (mSyncRoot) { if (mCallbacks.get(callingPid) != null) { CallbackRecord record = mCallbacks.get(callingPid); throw new SecurityException("The calling process has already " + "registered an IDisplayManagerCallback."); if (record != null) { record.updateEventsMask(eventsMask); return; } } CallbackRecord record = new CallbackRecord(callingPid, callingUid, callback); record = new CallbackRecord(callingPid, callingUid, callback, eventsMask); try { try { IBinder binder = callback.asBinder(); IBinder binder = callback.asBinder(); binder.linkToDeath(record, 0); binder.linkToDeath(record, 0); Loading Loading @@ -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); Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event); mHandler.sendMessage(msg); mHandler.sendMessage(msg); } } Loading Loading @@ -1724,7 +1729,8 @@ public final class DisplayManagerService extends SystemService { // Runs on Handler thread. // Runs on Handler thread. // Delivers display event notifications to callbacks. // 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) { if (DEBUG) { Slog.d(TAG, "Delivering display event: displayId=" Slog.d(TAG, "Delivering display event: displayId=" + displayId + ", event=" + event); + displayId + ", event=" + event); Loading Loading @@ -2059,13 +2065,20 @@ public final class DisplayManagerService extends SystemService { public final int mPid; public final int mPid; public final int mUid; public final int mUid; private final IDisplayManagerCallback mCallback; private final IDisplayManagerCallback mCallback; private @EventsMask AtomicLong mEventsMask; public boolean mWifiDisplayScanRequested; public boolean mWifiDisplayScanRequested; CallbackRecord(int pid, int uid, IDisplayManagerCallback callback) { CallbackRecord(int pid, int uid, IDisplayManagerCallback callback, @EventsMask long eventsMask) { mPid = pid; mPid = pid; mUid = uid; mUid = uid; mCallback = callback; mCallback = callback; mEventsMask = new AtomicLong(eventsMask); } public void updateEventsMask(@EventsMask long eventsMask) { mEventsMask.set(eventsMask); } } @Override @Override Loading @@ -2076,7 +2089,11 @@ public final class DisplayManagerService extends SystemService { onCallbackDied(this); onCallbackDied(this); } } public void notifyDisplayEventAsync(int displayId, int event) { public void notifyDisplayEventAsync(int displayId, @DisplayEvent int event) { if (!shouldSendEvent(event)) { return; } try { try { mCallback.onDisplayEvent(displayId, event); mCallback.onDisplayEvent(displayId, event); } catch (RemoteException ex) { } catch (RemoteException ex) { Loading @@ -2085,6 +2102,22 @@ public final class DisplayManagerService extends SystemService { binderDied(); 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 @VisibleForTesting Loading Loading @@ -2149,6 +2182,14 @@ public final class DisplayManagerService extends SystemService { @Override // Binder call @Override // Binder call public void registerCallback(IDisplayManagerCallback callback) { 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) { if (callback == null) { throw new IllegalArgumentException("listener must not be null"); throw new IllegalArgumentException("listener must not be null"); } } Loading @@ -2157,7 +2198,7 @@ public final class DisplayManagerService extends SystemService { final int callingUid = Binder.getCallingUid(); final int callingUid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); final long token = Binder.clearCallingIdentity(); try { try { registerCallbackInternal(callback, callingPid, callingUid); registerCallbackInternal(callback, callingPid, callingUid, eventsMask); } finally { } finally { Binder.restoreCallingIdentity(token); Binder.restoreCallingIdentity(token); } } Loading Loading
core/java/android/hardware/display/DisplayManager.java +62 −2 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import android.Manifest; import android.Manifest; import android.annotation.IntDef; import android.annotation.IntDef; import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.RequiresPermission; Loading Loading @@ -376,6 +377,43 @@ public final class DisplayManager { @TestApi @TestApi public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; 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 */ /** @hide */ public DisplayManager(Context context) { public DisplayManager(Context context) { Loading Loading @@ -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. * displays are added, removed or changed. * * * @param listener The listener to register. * @param listener The listener to register. Loading @@ -496,7 +534,29 @@ public final class DisplayManager { * @see #unregisterDisplayListener * @see #unregisterDisplayListener */ */ public void registerDisplayListener(DisplayListener listener, Handler handler) { 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); } } /** /** Loading
core/java/android/hardware/display/DisplayManagerGlobal.java +70 −15 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,9 @@ package android.hardware.display; package android.hardware.display; import static android.hardware.display.DisplayManager.EventsMask; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.app.PropertyInvalidatedCache; import android.app.PropertyInvalidatedCache; Loading @@ -42,6 +45,10 @@ import android.view.DisplayAdjustments; import android.view.DisplayInfo; import android.view.DisplayInfo; import android.view.Surface; 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.ArrayList; import java.util.Collections; import java.util.Collections; import java.util.List; import java.util.List; Loading @@ -66,6 +73,14 @@ public final class DisplayManagerGlobal { // orientation change before the display info cache has actually been invalidated. // orientation change before the display info cache has actually been invalidated. private static final boolean USE_CACHE = false; 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_ADDED = 1; public static final int EVENT_DISPLAY_CHANGED = 2; public static final int EVENT_DISPLAY_CHANGED = 2; public static final int EVENT_DISPLAY_REMOVED = 3; public static final int EVENT_DISPLAY_REMOVED = 3; Loading @@ -81,16 +96,17 @@ public final class DisplayManagerGlobal { private final IDisplayManager mDm; private final IDisplayManager mDm; private DisplayManagerCallback mCallback; private DisplayManagerCallback mCallback; private final ArrayList<DisplayListenerDelegate> mDisplayListeners = private @EventsMask long mRegisteredEventsMask = 0; new ArrayList<DisplayListenerDelegate>(); 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 final ColorSpace mWideColorSpace; private int[] mDisplayIdCache; private int[] mDisplayIdCache; private int mWifiDisplayScanNestCount; private int mWifiDisplayScanNestCount; private DisplayManagerGlobal(IDisplayManager dm) { @VisibleForTesting public DisplayManagerGlobal(IDisplayManager dm) { mDm = dm; mDm = dm; try { try { mWideColorSpace = mWideColorSpace = Loading Loading @@ -274,18 +290,25 @@ public final class DisplayManagerGlobal { * If that is still null, a runtime exception will be thrown. * If that is still null, a runtime exception will be thrown. */ */ public void registerDisplayListener(@NonNull DisplayListener listener, public void registerDisplayListener(@NonNull DisplayListener listener, @Nullable Handler handler) { @Nullable Handler handler, @EventsMask long eventsMask) { if (listener == null) { if (listener == null) { throw new IllegalArgumentException("listener must not be 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) { synchronized (mLock) { int index = findDisplayListenerLocked(listener); int index = findDisplayListenerLocked(listener); if (index < 0) { if (index < 0) { Looper looper = getLooperForHandler(handler); Looper looper = getLooperForHandler(handler); mDisplayListeners.add(new DisplayListenerDelegate(listener, looper)); mDisplayListeners.add(new DisplayListenerDelegate(listener, looper, eventsMask)); registerCallbackIfNeededLocked(); registerCallbackIfNeededLocked(); } else { mDisplayListeners.get(index).setEventsMask(eventsMask); } } updateCallbackIfNeededLocked(); } } } } Loading @@ -300,6 +323,7 @@ public final class DisplayManagerGlobal { DisplayListenerDelegate d = mDisplayListeners.get(index); DisplayListenerDelegate d = mDisplayListeners.get(index); d.clearEvents(); d.clearEvents(); mDisplayListeners.remove(index); mDisplayListeners.remove(index); updateCallbackIfNeededLocked(); } } } } } } Loading @@ -325,18 +349,36 @@ public final class DisplayManagerGlobal { return -1; 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() { private void registerCallbackIfNeededLocked() { if (mCallback == null) { if (mCallback == null) { mCallback = new DisplayManagerCallback(); mCallback = new DisplayManagerCallback(); updateCallbackIfNeededLocked(); } } private void updateCallbackIfNeededLocked() { int mask = calculateEventsMaskLocked(); if (mask != mRegisteredEventsMask) { try { try { mDm.registerCallback(mCallback); mDm.registerCallbackWithEventMask(mCallback, mask); mRegisteredEventsMask = mask; } catch (RemoteException ex) { } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); throw ex.rethrowFromSystemServer(); } } } } } } private void handleDisplayEvent(int displayId, int event) { private void handleDisplayEvent(int displayId, @DisplayEvent int event) { synchronized (mLock) { synchronized (mLock) { if (USE_CACHE) { if (USE_CACHE) { mDisplayInfoCache.remove(displayId); mDisplayInfoCache.remove(displayId); Loading Loading @@ -754,7 +796,7 @@ public final class DisplayManagerGlobal { private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { @Override @Override public void onDisplayEvent(int displayId, int event) { public void onDisplayEvent(int displayId, @DisplayEvent int event) { if (DEBUG) { if (DEBUG) { Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event); Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event); } } Loading @@ -764,13 +806,16 @@ public final class DisplayManagerGlobal { private static final class DisplayListenerDelegate extends Handler { private static final class DisplayListenerDelegate extends Handler { public final DisplayListener mListener; 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*/); super(looper, null, true /*async*/); mListener = listener; 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); Message msg = obtainMessage(event, displayId, 0); sendMessage(msg); sendMessage(msg); } } Loading @@ -779,17 +824,27 @@ public final class DisplayManagerGlobal { removeCallbacksAndMessages(null); removeCallbacksAndMessages(null); } } public synchronized void setEventsMask(@EventsMask long newEventsMask) { mEventsMask = newEventsMask; } @Override @Override public void handleMessage(Message msg) { public synchronized void handleMessage(Message msg) { switch (msg.what) { switch (msg.what) { case EVENT_DISPLAY_ADDED: case EVENT_DISPLAY_ADDED: if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) { mListener.onDisplayAdded(msg.arg1); mListener.onDisplayAdded(msg.arg1); } break; break; case EVENT_DISPLAY_CHANGED: case EVENT_DISPLAY_CHANGED: if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) { mListener.onDisplayChanged(msg.arg1); mListener.onDisplayChanged(msg.arg1); } break; break; case EVENT_DISPLAY_REMOVED: case EVENT_DISPLAY_REMOVED: if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) { mListener.onDisplayRemoved(msg.arg1); mListener.onDisplayRemoved(msg.arg1); } break; break; } } } } Loading
core/java/android/hardware/display/IDisplayManager.aidl +1 −0 Original line number Original line Diff line number Diff line Loading @@ -38,6 +38,7 @@ interface IDisplayManager { boolean isUidPresentOnDisplay(int uid, int displayId); boolean isUidPresentOnDisplay(int uid, int displayId); void registerCallback(in IDisplayManagerCallback callback); void registerCallback(in IDisplayManagerCallback callback); void registerCallbackWithEventMask(in IDisplayManagerCallback callback, long eventsMask); // Requires CONFIGURE_WIFI_DISPLAY permission. // Requires CONFIGURE_WIFI_DISPLAY permission. // The process must have previously registered a callback. // The process must have previously registered a callback. Loading
core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java 0 → 100644 +127 −0 Original line number Original line Diff line number Diff line /* * 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); } }
services/core/java/com/android/server/display/DisplayManagerService.java +51 −10 Original line number Original line Diff line number Diff line Loading @@ -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_SECURE_VIDEO_OUTPUT; import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT; import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; 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_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_CAN_SHOW_WITH_INSECURE_KEYGUARD; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; Loading @@ -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_SECURE; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; 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.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_EXTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL; import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL; Loading Loading @@ -125,6 +127,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Arrays; import java.util.Optional; import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.function.Consumer; /** /** Loading Loading @@ -820,14 +823,16 @@ public final class DisplayManagerService extends SystemService { } } private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid, private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid, int callingUid) { int callingUid, @EventsMask long eventsMask) { synchronized (mSyncRoot) { synchronized (mSyncRoot) { if (mCallbacks.get(callingPid) != null) { CallbackRecord record = mCallbacks.get(callingPid); throw new SecurityException("The calling process has already " + "registered an IDisplayManagerCallback."); if (record != null) { record.updateEventsMask(eventsMask); return; } } CallbackRecord record = new CallbackRecord(callingPid, callingUid, callback); record = new CallbackRecord(callingPid, callingUid, callback, eventsMask); try { try { IBinder binder = callback.asBinder(); IBinder binder = callback.asBinder(); binder.linkToDeath(record, 0); binder.linkToDeath(record, 0); Loading Loading @@ -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); Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event); mHandler.sendMessage(msg); mHandler.sendMessage(msg); } } Loading Loading @@ -1724,7 +1729,8 @@ public final class DisplayManagerService extends SystemService { // Runs on Handler thread. // Runs on Handler thread. // Delivers display event notifications to callbacks. // 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) { if (DEBUG) { Slog.d(TAG, "Delivering display event: displayId=" Slog.d(TAG, "Delivering display event: displayId=" + displayId + ", event=" + event); + displayId + ", event=" + event); Loading Loading @@ -2059,13 +2065,20 @@ public final class DisplayManagerService extends SystemService { public final int mPid; public final int mPid; public final int mUid; public final int mUid; private final IDisplayManagerCallback mCallback; private final IDisplayManagerCallback mCallback; private @EventsMask AtomicLong mEventsMask; public boolean mWifiDisplayScanRequested; public boolean mWifiDisplayScanRequested; CallbackRecord(int pid, int uid, IDisplayManagerCallback callback) { CallbackRecord(int pid, int uid, IDisplayManagerCallback callback, @EventsMask long eventsMask) { mPid = pid; mPid = pid; mUid = uid; mUid = uid; mCallback = callback; mCallback = callback; mEventsMask = new AtomicLong(eventsMask); } public void updateEventsMask(@EventsMask long eventsMask) { mEventsMask.set(eventsMask); } } @Override @Override Loading @@ -2076,7 +2089,11 @@ public final class DisplayManagerService extends SystemService { onCallbackDied(this); onCallbackDied(this); } } public void notifyDisplayEventAsync(int displayId, int event) { public void notifyDisplayEventAsync(int displayId, @DisplayEvent int event) { if (!shouldSendEvent(event)) { return; } try { try { mCallback.onDisplayEvent(displayId, event); mCallback.onDisplayEvent(displayId, event); } catch (RemoteException ex) { } catch (RemoteException ex) { Loading @@ -2085,6 +2102,22 @@ public final class DisplayManagerService extends SystemService { binderDied(); 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 @VisibleForTesting Loading Loading @@ -2149,6 +2182,14 @@ public final class DisplayManagerService extends SystemService { @Override // Binder call @Override // Binder call public void registerCallback(IDisplayManagerCallback callback) { 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) { if (callback == null) { throw new IllegalArgumentException("listener must not be null"); throw new IllegalArgumentException("listener must not be null"); } } Loading @@ -2157,7 +2198,7 @@ public final class DisplayManagerService extends SystemService { final int callingUid = Binder.getCallingUid(); final int callingUid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); final long token = Binder.clearCallingIdentity(); try { try { registerCallbackInternal(callback, callingPid, callingUid); registerCallbackInternal(callback, callingPid, callingUid, eventsMask); } finally { } finally { Binder.restoreCallingIdentity(token); Binder.restoreCallingIdentity(token); } } Loading