1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.display;
18 
19 
20 import static android.hardware.display.DisplayManager.EventsMask;
21 import static android.view.Display.HdrCapabilities.HdrType;
22 
23 import android.Manifest;
24 import android.annotation.IntDef;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.RequiresPermission;
28 import android.app.ActivityThread;
29 import android.app.PropertyInvalidatedCache;
30 import android.compat.annotation.UnsupportedAppUsage;
31 import android.content.Context;
32 import android.content.pm.ParceledListSlice;
33 import android.content.res.Resources;
34 import android.graphics.ColorSpace;
35 import android.graphics.Point;
36 import android.hardware.OverlayProperties;
37 import android.hardware.display.DisplayManager.DisplayListener;
38 import android.hardware.graphics.common.DisplayDecorationSupport;
39 import android.media.projection.IMediaProjection;
40 import android.media.projection.MediaProjection;
41 import android.os.Binder;
42 import android.os.Handler;
43 import android.os.HandlerExecutor;
44 import android.os.IBinder;
45 import android.os.Looper;
46 import android.os.RemoteException;
47 import android.os.ServiceManager;
48 import android.os.Trace;
49 import android.sysprop.DisplayProperties;
50 import android.text.TextUtils;
51 import android.util.Log;
52 import android.util.Pair;
53 import android.util.Slog;
54 import android.util.SparseArray;
55 import android.view.Display;
56 import android.view.DisplayAdjustments;
57 import android.view.DisplayInfo;
58 import android.view.Surface;
59 
60 import com.android.internal.annotations.VisibleForTesting;
61 
62 import java.lang.annotation.Retention;
63 import java.lang.annotation.RetentionPolicy;
64 import java.util.Collections;
65 import java.util.List;
66 import java.util.Objects;
67 import java.util.concurrent.CopyOnWriteArrayList;
68 import java.util.concurrent.Executor;
69 import java.util.concurrent.atomic.AtomicLong;
70 
71 /**
72  * Manager communication with the display manager service on behalf of
73  * an application process.  You're probably looking for {@link DisplayManager}.
74  *
75  * @hide
76  */
77 public final class DisplayManagerGlobal {
78     private static final String TAG = "DisplayManager";
79 
80     private static final String EXTRA_LOGGING_PACKAGE_NAME =
81             DisplayProperties.debug_vri_package().orElse(null);
82     private static String sCurrentPackageName = ActivityThread.currentPackageName();
83     private static boolean sExtraDisplayListenerLogging = initExtraLogging();
84 
85     // To enable these logs, run:
86     // 'adb shell setprop persist.log.tag.DisplayManager DEBUG && adb reboot'
87     private static final boolean DEBUG = DisplayManager.DEBUG || sExtraDisplayListenerLogging;
88 
89     // True if display info and display ids should be cached.
90     //
91     // FIXME: The cache is currently disabled because it's unclear whether we have the
92     // necessary guarantees that the caches will always be flushed before clients
93     // attempt to observe their new state.  For example, depending on the order
94     // in which the binder transactions take place, we might have a problem where
95     // an application could start processing a configuration change due to a display
96     // orientation change before the display info cache has actually been invalidated.
97     private static final boolean USE_CACHE = false;
98 
99     @IntDef(prefix = {"EVENT_DISPLAY_"}, flag = true, value = {
100             EVENT_DISPLAY_ADDED,
101             EVENT_DISPLAY_CHANGED,
102             EVENT_DISPLAY_REMOVED,
103             EVENT_DISPLAY_BRIGHTNESS_CHANGED,
104             EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED,
105             EVENT_DISPLAY_CONNECTED,
106             EVENT_DISPLAY_DISCONNECTED,
107     })
108     @Retention(RetentionPolicy.SOURCE)
109     public @interface DisplayEvent {}
110 
111     public static final int EVENT_DISPLAY_ADDED = 1;
112     public static final int EVENT_DISPLAY_CHANGED = 2;
113     public static final int EVENT_DISPLAY_REMOVED = 3;
114     public static final int EVENT_DISPLAY_BRIGHTNESS_CHANGED = 4;
115     public static final int EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED = 5;
116     public static final int EVENT_DISPLAY_CONNECTED = 6;
117     public static final int EVENT_DISPLAY_DISCONNECTED = 7;
118 
119     @UnsupportedAppUsage
120     private static DisplayManagerGlobal sInstance;
121 
122     // Guarded by mLock
123     private boolean mDispatchNativeCallbacks = false;
124     private float mNativeCallbackReportedRefreshRate;
125     private final Object mLock = new Object();
126 
127     @UnsupportedAppUsage
128     private final IDisplayManager mDm;
129 
130     private DisplayManagerCallback mCallback;
131     private @EventsMask long mRegisteredEventsMask = 0;
132     private final CopyOnWriteArrayList<DisplayListenerDelegate> mDisplayListeners =
133             new CopyOnWriteArrayList<>();
134 
135     private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<>();
136     private final ColorSpace mWideColorSpace;
137     private final OverlayProperties mOverlayProperties;
138     private int[] mDisplayIdCache;
139 
140     private int mWifiDisplayScanNestCount;
141 
142     private final Binder mToken = new Binder();
143 
144     @VisibleForTesting
DisplayManagerGlobal(IDisplayManager dm)145     public DisplayManagerGlobal(IDisplayManager dm) {
146         mDm = dm;
147         initExtraLogging();
148 
149         try {
150             mWideColorSpace =
151                     ColorSpace.get(
152                             ColorSpace.Named.values()[mDm.getPreferredWideGamutColorSpaceId()]);
153             mOverlayProperties = mDm.getOverlaySupport();
154         } catch (RemoteException ex) {
155             throw ex.rethrowFromSystemServer();
156         }
157     }
158 
159     private PropertyInvalidatedCache<Integer, DisplayInfo> mDisplayCache =
160             new PropertyInvalidatedCache<Integer, DisplayInfo>(
161                 8, // size of display cache
162                 CACHE_KEY_DISPLAY_INFO_PROPERTY) {
163                 @Override
164                 public DisplayInfo recompute(Integer id) {
165                     try {
166                         return mDm.getDisplayInfo(id);
167                     } catch (RemoteException ex) {
168                         throw ex.rethrowFromSystemServer();
169                     }
170                 }
171             };
172 
173     /**
174      * Gets an instance of the display manager global singleton.
175      *
176      * @return The display manager instance, may be null early in system startup
177      * before the display manager has been fully initialized.
178      */
179     @UnsupportedAppUsage
getInstance()180     public static DisplayManagerGlobal getInstance() {
181         synchronized (DisplayManagerGlobal.class) {
182             if (sInstance == null) {
183                 IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
184                 if (b != null) {
185                     sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
186                 }
187             }
188             return sInstance;
189         }
190     }
191 
192     /**
193      * Get information about a particular logical display.
194      *
195      * @param displayId The logical display id.
196      * @return Information about the specified display, or null if it does not exist.
197      * This object belongs to an internal cache and should be treated as if it were immutable.
198      */
199     @UnsupportedAppUsage
getDisplayInfo(int displayId)200     public DisplayInfo getDisplayInfo(int displayId) {
201         synchronized (mLock) {
202             return getDisplayInfoLocked(displayId);
203         }
204     }
205 
206     /**
207      * Gets information about a particular logical display
208      * See {@link getDisplayInfo}, but assumes that {@link mLock} is held
209      */
getDisplayInfoLocked(int displayId)210     private @Nullable DisplayInfo getDisplayInfoLocked(int displayId) {
211         DisplayInfo info = null;
212         if (mDisplayCache != null) {
213             info = mDisplayCache.query(displayId);
214         } else {
215             try {
216                 info = mDm.getDisplayInfo(displayId);
217             } catch (RemoteException ex) {
218                 ex.rethrowFromSystemServer();
219             }
220         }
221         if (info == null) {
222             return null;
223         }
224 
225         registerCallbackIfNeededLocked();
226 
227         if (DEBUG) {
228             Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
229         }
230         return info;
231     }
232 
233     /**
234      * Gets all currently valid logical display ids.
235      *
236      * @return An array containing all display ids.
237      */
238     @UnsupportedAppUsage
getDisplayIds()239     public int[] getDisplayIds() {
240         return getDisplayIds(/* includeDisabled= */ false);
241     }
242 
243     /**
244      * Gets all currently valid logical display ids.
245      *
246      * @param includeDisabled True if the returned list of displays includes disabled displays.
247      * @return An array containing all display ids.
248      */
getDisplayIds(boolean includeDisabled)249     public int[] getDisplayIds(boolean includeDisabled) {
250         try {
251             synchronized (mLock) {
252                 if (USE_CACHE) {
253                     if (mDisplayIdCache != null) {
254                         return mDisplayIdCache;
255                     }
256                 }
257 
258                 int[] displayIds = mDm.getDisplayIds(includeDisabled);
259                 if (USE_CACHE) {
260                     mDisplayIdCache = displayIds;
261                 }
262                 registerCallbackIfNeededLocked();
263                 return displayIds;
264             }
265         } catch (RemoteException ex) {
266             throw ex.rethrowFromSystemServer();
267         }
268     }
269 
270     /**
271      * Check if specified UID's content is present on display and should be granted access to it.
272      *
273      * @param uid UID to be checked.
274      * @param displayId id of the display where presence of the content is checked.
275      * @return {@code true} if UID is present on display, {@code false} otherwise.
276      */
isUidPresentOnDisplay(int uid, int displayId)277     public boolean isUidPresentOnDisplay(int uid, int displayId) {
278         try {
279             return mDm.isUidPresentOnDisplay(uid, displayId);
280         } catch (RemoteException ex) {
281             throw ex.rethrowFromSystemServer();
282         }
283     }
284 
285     /**
286      * Gets information about a logical display.
287      *
288      * The display metrics may be adjusted to provide compatibility
289      * for legacy applications or limited screen areas.
290      *
291      * @param displayId The logical display id.
292      * @param daj The compatibility info and activityToken.
293      * @return The display object, or null if there is no display with the given id.
294      */
getCompatibleDisplay(int displayId, DisplayAdjustments daj)295     public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
296         DisplayInfo displayInfo = getDisplayInfo(displayId);
297         if (displayInfo == null) {
298             return null;
299         }
300         return new Display(this, displayId, displayInfo, daj);
301     }
302 
303     /**
304      * Gets information about a logical display.
305      *
306      * The display metrics may be adjusted to provide compatibility
307      * for legacy applications or limited screen areas.
308      *
309      * @param displayId The logical display id.
310      * @param resources Resources providing compatibility info.
311      * @return The display object, or null if there is no display with the given id.
312      */
getCompatibleDisplay(int displayId, Resources resources)313     public Display getCompatibleDisplay(int displayId, Resources resources) {
314         DisplayInfo displayInfo = getDisplayInfo(displayId);
315         if (displayInfo == null) {
316             return null;
317         }
318         return new Display(this, displayId, displayInfo, resources);
319     }
320 
321     /**
322      * Gets information about a logical display without applying any compatibility metrics.
323      *
324      * @param displayId The logical display id.
325      * @return The display object, or null if there is no display with the given id.
326      */
327     @UnsupportedAppUsage
getRealDisplay(int displayId)328     public Display getRealDisplay(int displayId) {
329         return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
330     }
331 
332     /**
333      * Register a listener for display-related changes.
334      *
335      * @param listener The listener that will be called when display changes occur.
336      * @param handler Handler for the thread that will be receiving the callbacks. May be null.
337      * If null, listener will use the handler for the current thread, and if still null,
338      * the handler for the main thread.
339      * If that is still null, a runtime exception will be thrown.
340      * @param packageName of the calling package.
341      */
registerDisplayListener(@onNull DisplayListener listener, @Nullable Handler handler, @EventsMask long eventsMask, String packageName)342     public void registerDisplayListener(@NonNull DisplayListener listener,
343             @Nullable Handler handler, @EventsMask long eventsMask, String packageName) {
344         Looper looper = getLooperForHandler(handler);
345         Handler springBoard = new Handler(looper);
346         registerDisplayListener(listener, new HandlerExecutor(springBoard), eventsMask,
347                 packageName);
348     }
349 
350     /**
351      * Register a listener for display-related changes.
352      *
353      * @param listener The listener that will be called when display changes occur.
354      * @param executor Executor for the thread that will be receiving the callbacks. Cannot be null.
355      * @param eventsMask Mask of events to be listened to.
356      * @param packageName of the calling package.
357      */
registerDisplayListener(@onNull DisplayListener listener, @NonNull Executor executor, @EventsMask long eventsMask, String packageName)358     public void registerDisplayListener(@NonNull DisplayListener listener,
359             @NonNull Executor executor, @EventsMask long eventsMask, String packageName) {
360         if (listener == null) {
361             throw new IllegalArgumentException("listener must not be null");
362         }
363 
364         if (eventsMask == 0) {
365             throw new IllegalArgumentException("The set of events to listen to must not be empty.");
366         }
367 
368         if (extraLogging()) {
369             Slog.i(TAG, "Registering Display Listener: "
370                     + Long.toBinaryString(eventsMask) + ", packageName: " + packageName);
371         }
372 
373         synchronized (mLock) {
374             int index = findDisplayListenerLocked(listener);
375             if (index < 0) {
376                 mDisplayListeners.add(new DisplayListenerDelegate(listener, executor, eventsMask,
377                         packageName));
378                 registerCallbackIfNeededLocked();
379             } else {
380                 mDisplayListeners.get(index).setEventsMask(eventsMask);
381             }
382             updateCallbackIfNeededLocked();
383             maybeLogAllDisplayListeners();
384         }
385     }
386 
unregisterDisplayListener(DisplayListener listener)387     public void unregisterDisplayListener(DisplayListener listener) {
388         if (listener == null) {
389             throw new IllegalArgumentException("listener must not be null");
390         }
391 
392         if (extraLogging()) {
393             Slog.i(TAG, "Unregistering Display Listener: " + listener);
394         }
395 
396         synchronized (mLock) {
397             int index = findDisplayListenerLocked(listener);
398             if (index >= 0) {
399                 DisplayListenerDelegate d = mDisplayListeners.get(index);
400                 d.clearEvents();
401                 mDisplayListeners.remove(index);
402                 updateCallbackIfNeededLocked();
403             }
404         }
405         maybeLogAllDisplayListeners();
406     }
407 
maybeLogAllDisplayListeners()408     private void maybeLogAllDisplayListeners() {
409         if (!extraLogging()) {
410             return;
411         }
412 
413         Slog.i(TAG, "Currently Registered Display Listeners:");
414         for (int i = 0; i < mDisplayListeners.size(); i++) {
415             Slog.i(TAG, i + ": " + mDisplayListeners.get(i));
416         }
417     }
418 
419     /**
420      * Called when there is a display-related window configuration change. Reroutes the event from
421      * WindowManager to make sure the {@link Display} fields are up-to-date in the last callback.
422      * @param displayId the logical display that was changed.
423      */
handleDisplayChangeFromWindowManager(int displayId)424     public void handleDisplayChangeFromWindowManager(int displayId) {
425         // There can be racing condition between DMS and WMS callbacks, so force triggering the
426         // listener to make sure the client can get the onDisplayChanged callback even if
427         // DisplayInfo is not changed (Display read from both DisplayInfo and WindowConfiguration).
428         handleDisplayEvent(displayId, EVENT_DISPLAY_CHANGED, true /* forceUpdate */);
429     }
430 
getLooperForHandler(@ullable Handler handler)431     private static Looper getLooperForHandler(@Nullable Handler handler) {
432         Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
433         if (looper == null) {
434             looper = Looper.getMainLooper();
435         }
436         if (looper == null) {
437             throw new RuntimeException("Could not get Looper for the UI thread.");
438         }
439         return looper;
440     }
441 
findDisplayListenerLocked(DisplayListener listener)442     private int findDisplayListenerLocked(DisplayListener listener) {
443         final int numListeners = mDisplayListeners.size();
444         for (int i = 0; i < numListeners; i++) {
445             if (mDisplayListeners.get(i).mListener == listener) {
446                 return i;
447             }
448         }
449         return -1;
450     }
451 
452     @EventsMask
calculateEventsMaskLocked()453     private int calculateEventsMaskLocked() {
454         int mask = 0;
455         final int numListeners = mDisplayListeners.size();
456         for (int i = 0; i < numListeners; i++) {
457             mask |= mDisplayListeners.get(i).mEventsMask;
458         }
459         if (mDispatchNativeCallbacks) {
460             mask |= DisplayManager.EVENT_FLAG_DISPLAY_ADDED
461                     | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
462                     | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
463         }
464         return mask;
465     }
466 
registerCallbackIfNeededLocked()467     private void registerCallbackIfNeededLocked() {
468         if (mCallback == null) {
469             mCallback = new DisplayManagerCallback();
470             updateCallbackIfNeededLocked();
471         }
472     }
473 
updateCallbackIfNeededLocked()474     private void updateCallbackIfNeededLocked() {
475         int mask = calculateEventsMaskLocked();
476         if (DEBUG) {
477             Log.d(TAG, "Mask for listener: " + mask);
478         }
479         if (mask != mRegisteredEventsMask) {
480             try {
481                 mDm.registerCallbackWithEventMask(mCallback, mask);
482                 mRegisteredEventsMask = mask;
483             } catch (RemoteException ex) {
484                 throw ex.rethrowFromSystemServer();
485             }
486         }
487     }
488 
handleDisplayEvent(int displayId, @DisplayEvent int event, boolean forceUpdate)489     private void handleDisplayEvent(int displayId, @DisplayEvent int event, boolean forceUpdate) {
490         final DisplayInfo info;
491         synchronized (mLock) {
492             if (USE_CACHE) {
493                 mDisplayInfoCache.remove(displayId);
494 
495                 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
496                     mDisplayIdCache = null;
497                 }
498             }
499 
500             info = getDisplayInfoLocked(displayId);
501             if (event == EVENT_DISPLAY_CHANGED && mDispatchNativeCallbacks) {
502                 // Choreographer only supports a single display, so only dispatch refresh rate
503                 // changes for the default display.
504                 if (displayId == Display.DEFAULT_DISPLAY) {
505                     // We can likely save a binder hop if we attach the refresh rate onto the
506                     // listener.
507                     DisplayInfo display = getDisplayInfoLocked(displayId);
508                     if (display != null
509                             && mNativeCallbackReportedRefreshRate != display.getRefreshRate()) {
510                         mNativeCallbackReportedRefreshRate = display.getRefreshRate();
511                         // Signal native callbacks if we ever set a refresh rate.
512                         nSignalNativeCallbacks(mNativeCallbackReportedRefreshRate);
513                     }
514                 }
515             }
516         }
517         // Accepting an Executor means the listener may be synchronously invoked, so we must
518         // not be holding mLock when we do so
519         for (DisplayListenerDelegate listener : mDisplayListeners) {
520             listener.sendDisplayEvent(displayId, event, info, forceUpdate);
521         }
522     }
523 
524     /**
525      * Enable a connected display that is currently disabled.
526      * @hide
527      */
528     @RequiresPermission("android.permission.MANAGE_DISPLAYS")
enableConnectedDisplay(int displayId)529     public void enableConnectedDisplay(int displayId) {
530         try {
531             mDm.enableConnectedDisplay(displayId);
532         } catch (RemoteException ex) {
533             Log.e(TAG, "Error trying to enable external display", ex);
534         }
535     }
536 
537 
538     /**
539      * Disable a connected display that is currently enabled.
540      * @hide
541      */
542     @RequiresPermission("android.permission.MANAGE_DISPLAYS")
disableConnectedDisplay(int displayId)543     public void disableConnectedDisplay(int displayId) {
544         try {
545             mDm.disableConnectedDisplay(displayId);
546         } catch (RemoteException ex) {
547             Log.e(TAG, "Error trying to enable external display", ex);
548         }
549     }
550 
551     /**
552      * Request to power a display ON or OFF.
553      * @hide
554      */
555     @RequiresPermission("android.permission.MANAGE_DISPLAYS")
requestDisplayPower(int displayId, boolean on)556     public boolean requestDisplayPower(int displayId, boolean on) {
557         try {
558             return mDm.requestDisplayPower(displayId, on);
559         } catch (RemoteException ex) {
560             Log.e(TAG, "Error trying to request display power " + on, ex);
561             return false;
562         }
563     }
564 
startWifiDisplayScan()565     public void startWifiDisplayScan() {
566         synchronized (mLock) {
567             if (mWifiDisplayScanNestCount++ == 0) {
568                 registerCallbackIfNeededLocked();
569                 try {
570                     mDm.startWifiDisplayScan();
571                 } catch (RemoteException ex) {
572                     throw ex.rethrowFromSystemServer();
573                 }
574             }
575         }
576     }
577 
stopWifiDisplayScan()578     public void stopWifiDisplayScan() {
579         synchronized (mLock) {
580             if (--mWifiDisplayScanNestCount == 0) {
581                 try {
582                     mDm.stopWifiDisplayScan();
583                 } catch (RemoteException ex) {
584                     throw ex.rethrowFromSystemServer();
585                 }
586             } else if (mWifiDisplayScanNestCount < 0) {
587                 Log.wtf(TAG, "Wifi display scan nest count became negative: "
588                         + mWifiDisplayScanNestCount);
589                 mWifiDisplayScanNestCount = 0;
590             }
591         }
592     }
593 
connectWifiDisplay(String deviceAddress)594     public void connectWifiDisplay(String deviceAddress) {
595         if (deviceAddress == null) {
596             throw new IllegalArgumentException("deviceAddress must not be null");
597         }
598 
599         try {
600             mDm.connectWifiDisplay(deviceAddress);
601         } catch (RemoteException ex) {
602             throw ex.rethrowFromSystemServer();
603         }
604     }
605 
pauseWifiDisplay()606     public void pauseWifiDisplay() {
607         try {
608             mDm.pauseWifiDisplay();
609         } catch (RemoteException ex) {
610             throw ex.rethrowFromSystemServer();
611         }
612     }
613 
resumeWifiDisplay()614     public void resumeWifiDisplay() {
615         try {
616             mDm.resumeWifiDisplay();
617         } catch (RemoteException ex) {
618             throw ex.rethrowFromSystemServer();
619         }
620     }
621 
622     @UnsupportedAppUsage
disconnectWifiDisplay()623     public void disconnectWifiDisplay() {
624         try {
625             mDm.disconnectWifiDisplay();
626         } catch (RemoteException ex) {
627             throw ex.rethrowFromSystemServer();
628         }
629     }
630 
renameWifiDisplay(String deviceAddress, String alias)631     public void renameWifiDisplay(String deviceAddress, String alias) {
632         if (deviceAddress == null) {
633             throw new IllegalArgumentException("deviceAddress must not be null");
634         }
635 
636         try {
637             mDm.renameWifiDisplay(deviceAddress, alias);
638         } catch (RemoteException ex) {
639             throw ex.rethrowFromSystemServer();
640         }
641     }
642 
forgetWifiDisplay(String deviceAddress)643     public void forgetWifiDisplay(String deviceAddress) {
644         if (deviceAddress == null) {
645             throw new IllegalArgumentException("deviceAddress must not be null");
646         }
647 
648         try {
649             mDm.forgetWifiDisplay(deviceAddress);
650         } catch (RemoteException ex) {
651             throw ex.rethrowFromSystemServer();
652         }
653     }
654 
655     @UnsupportedAppUsage
getWifiDisplayStatus()656     public WifiDisplayStatus getWifiDisplayStatus() {
657         try {
658             return mDm.getWifiDisplayStatus();
659         } catch (RemoteException ex) {
660             throw ex.rethrowFromSystemServer();
661         }
662     }
663 
664     /**
665      * Sets the HDR types that have been disabled by user.
666      * @param userDisabledHdrTypes the HDR types to disable. The HDR types are any of
667      */
setUserDisabledHdrTypes(@drType int[] userDisabledHdrTypes)668     public void setUserDisabledHdrTypes(@HdrType int[] userDisabledHdrTypes) {
669         try {
670             mDm.setUserDisabledHdrTypes(userDisabledHdrTypes);
671         } catch (RemoteException ex) {
672             throw ex.rethrowFromSystemServer();
673         }
674     }
675 
676     /**
677      * Sets whether or not the user disabled HDR types are returned from
678      * {@link Display#getHdrCapabilities}.
679      *
680      * @param areUserDisabledHdrTypesAllowed If true, the user-disabled
681      * types are ignored and returned, if the display supports them. If
682      * false, the user-disabled types are taken into consideration and
683      * are never returned, even if the display supports them.
684      */
setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed)685     public void setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed) {
686         try {
687             mDm.setAreUserDisabledHdrTypesAllowed(areUserDisabledHdrTypesAllowed);
688         } catch (RemoteException ex) {
689             throw ex.rethrowFromSystemServer();
690         }
691     }
692 
693     /**
694      * Returns whether or not the user-disabled HDR types are returned from
695      * {@link Display#getHdrCapabilities}.
696      */
areUserDisabledHdrTypesAllowed()697     public boolean areUserDisabledHdrTypesAllowed() {
698         try {
699             return mDm.areUserDisabledHdrTypesAllowed();
700         } catch (RemoteException ex) {
701             throw ex.rethrowFromSystemServer();
702         }
703     }
704 
705     /**
706      * Returns the HDR formats disabled by the user.
707      *
708      */
getUserDisabledHdrTypes()709     public int[] getUserDisabledHdrTypes() {
710         try {
711             return mDm.getUserDisabledHdrTypes();
712         } catch (RemoteException ex) {
713             throw ex.rethrowFromSystemServer();
714         }
715     }
716 
717     /**
718      * Overrides HDR modes for a display device.
719      *
720      */
721     @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
overrideHdrTypes(int displayId, int[] modes)722     public void overrideHdrTypes(int displayId, int[] modes) {
723         try {
724             mDm.overrideHdrTypes(displayId, modes);
725         } catch (RemoteException ex) {
726             throw ex.rethrowFromSystemServer();
727         }
728     }
729 
730 
requestColorMode(int displayId, int colorMode)731     public void requestColorMode(int displayId, int colorMode) {
732         try {
733             mDm.requestColorMode(displayId, colorMode);
734         } catch (RemoteException ex) {
735             throw ex.rethrowFromSystemServer();
736         }
737     }
738 
createVirtualDisplay(@onNull Context context, MediaProjection projection, @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback, @Nullable Executor executor)739     public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection,
740             @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback,
741             @Nullable Executor executor) {
742         VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, executor);
743         IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
744         int displayId;
745         try {
746             displayId = mDm.createVirtualDisplay(virtualDisplayConfig, callbackWrapper,
747                     projectionToken, context.getPackageName());
748         } catch (RemoteException ex) {
749             throw ex.rethrowFromSystemServer();
750         }
751         return createVirtualDisplayWrapper(virtualDisplayConfig, callbackWrapper,
752                 displayId);
753     }
754 
755     /**
756      * Create a VirtualDisplay wrapper object for a newly created virtual display ; to be called
757      * once the display has been created in system_server.
758      */
759     @Nullable
createVirtualDisplayWrapper(VirtualDisplayConfig virtualDisplayConfig, IVirtualDisplayCallback callbackWrapper, int displayId)760     public VirtualDisplay createVirtualDisplayWrapper(VirtualDisplayConfig virtualDisplayConfig,
761             IVirtualDisplayCallback callbackWrapper, int displayId) {
762         if (displayId < 0) {
763             Log.e(TAG, "Could not create virtual display: " + virtualDisplayConfig.getName());
764             return null;
765         }
766         Display display = getRealDisplay(displayId);
767         if (display == null) {
768             Log.wtf(TAG, "Could not obtain display info for newly created "
769                     + "virtual display: " + virtualDisplayConfig.getName());
770             try {
771                 mDm.releaseVirtualDisplay(callbackWrapper);
772             } catch (RemoteException ex) {
773                 throw ex.rethrowFromSystemServer();
774             }
775             return null;
776         }
777         return new VirtualDisplay(this, display, callbackWrapper,
778                 virtualDisplayConfig.getSurface());
779     }
780 
setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface)781     public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
782         try {
783             mDm.setVirtualDisplaySurface(token, surface);
784             setVirtualDisplayState(token, surface != null);
785         } catch (RemoteException ex) {
786             throw ex.rethrowFromSystemServer();
787         }
788     }
789 
resizeVirtualDisplay(IVirtualDisplayCallback token, int width, int height, int densityDpi)790     public void resizeVirtualDisplay(IVirtualDisplayCallback token,
791             int width, int height, int densityDpi) {
792         try {
793             mDm.resizeVirtualDisplay(token, width, height, densityDpi);
794         } catch (RemoteException ex) {
795             throw ex.rethrowFromSystemServer();
796         }
797     }
798 
releaseVirtualDisplay(IVirtualDisplayCallback token)799     public void releaseVirtualDisplay(IVirtualDisplayCallback token) {
800         try {
801             mDm.releaseVirtualDisplay(token);
802         } catch (RemoteException ex) {
803             throw ex.rethrowFromSystemServer();
804         }
805     }
806 
setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn)807     void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) {
808         try {
809             mDm.setVirtualDisplayState(token, isOn);
810         } catch (RemoteException ex) {
811             throw ex.rethrowFromSystemServer();
812         }
813     }
814 
815     /**
816      * Gets the stable device display size, in pixels.
817      */
getStableDisplaySize()818     public Point getStableDisplaySize() {
819         try {
820             return mDm.getStableDisplaySize();
821         } catch (RemoteException ex) {
822             throw ex.rethrowFromSystemServer();
823         }
824     }
825 
826     /**
827      * Retrieves brightness change events.
828      */
getBrightnessEvents(String callingPackage)829     public List<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
830         try {
831             ParceledListSlice<BrightnessChangeEvent> events =
832                     mDm.getBrightnessEvents(callingPackage);
833             if (events == null) {
834                 return Collections.emptyList();
835             }
836             return events.getList();
837         } catch (RemoteException ex) {
838             throw ex.rethrowFromSystemServer();
839         }
840     }
841 
842     /**
843      * Retrieves Brightness Info for the specified display.
844      */
getBrightnessInfo(int displayId)845     public BrightnessInfo getBrightnessInfo(int displayId) {
846         try {
847             return mDm.getBrightnessInfo(displayId);
848         } catch (RemoteException ex) {
849             throw ex.rethrowFromSystemServer();
850         }
851     }
852 
853     /**
854      * Gets the preferred wide gamut color space for all displays.
855      * The wide gamut color space is returned from composition pipeline
856      * based on hardware capability.
857      *
858      * @hide
859      */
getPreferredWideGamutColorSpace()860     public ColorSpace getPreferredWideGamutColorSpace() {
861         return mWideColorSpace;
862     }
863 
864     /**
865      * Gets the overlay properties for all displays.
866      *
867      * @hide
868      */
getOverlaySupport()869     public OverlayProperties getOverlaySupport() {
870         return mOverlayProperties;
871     }
872 
873     /**
874      * Sets the global brightness configuration for a given user.
875      *
876      * @hide
877      */
setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId, String packageName)878     public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId,
879             String packageName) {
880         try {
881             mDm.setBrightnessConfigurationForUser(c, userId, packageName);
882         } catch (RemoteException ex) {
883             throw ex.rethrowFromSystemServer();
884         }
885     }
886 
887     /**
888      * Sets the brightness configuration for a given display.
889      *
890      * @hide
891      */
setBrightnessConfigurationForDisplay(BrightnessConfiguration c, String uniqueDisplayId, int userId, String packageName)892     public void setBrightnessConfigurationForDisplay(BrightnessConfiguration c,
893             String uniqueDisplayId, int userId, String packageName) {
894         try {
895             mDm.setBrightnessConfigurationForDisplay(c, uniqueDisplayId, userId, packageName);
896         } catch (RemoteException ex) {
897             throw ex.rethrowFromSystemServer();
898         }
899     }
900 
901     /**
902      * Gets the brightness configuration for a given display or null if one hasn't been set.
903      *
904      * @hide
905      */
getBrightnessConfigurationForDisplay(String uniqueDisplayId, int userId)906     public BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueDisplayId,
907             int userId) {
908         try {
909             return mDm.getBrightnessConfigurationForDisplay(uniqueDisplayId, userId);
910         } catch (RemoteException ex) {
911             throw ex.rethrowFromSystemServer();
912         }
913     }
914 
915     /**
916      * Gets the global brightness configuration for a given user or null if one hasn't been set.
917      *
918      * @hide
919      */
getBrightnessConfigurationForUser(int userId)920     public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
921         try {
922             return mDm.getBrightnessConfigurationForUser(userId);
923         } catch (RemoteException ex) {
924             throw ex.rethrowFromSystemServer();
925         }
926     }
927 
928     /**
929      * Gets the default brightness configuration or null if one hasn't been configured.
930      *
931      * @hide
932      */
getDefaultBrightnessConfiguration()933     public BrightnessConfiguration getDefaultBrightnessConfiguration() {
934         try {
935             return mDm.getDefaultBrightnessConfiguration();
936         } catch (RemoteException ex) {
937             throw ex.rethrowFromSystemServer();
938         }
939     }
940 
941     /**
942      * Gets the last requested minimal post processing setting for the display with displayId.
943      *
944      * @hide
945      */
isMinimalPostProcessingRequested(int displayId)946     public boolean isMinimalPostProcessingRequested(int displayId) {
947         try {
948             return mDm.isMinimalPostProcessingRequested(displayId);
949         } catch (RemoteException ex) {
950             throw ex.rethrowFromSystemServer();
951         }
952     }
953 
954     /**
955      * Temporarily sets the brightness of the display.
956      * <p>
957      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
958      * </p>
959      *
960      * @param brightness The brightness value from 0.0f to 1.0f.
961      *
962      * @hide Requires signature permission.
963      */
setTemporaryBrightness(int displayId, float brightness)964     public void setTemporaryBrightness(int displayId, float brightness) {
965         try {
966             mDm.setTemporaryBrightness(displayId, brightness);
967         } catch (RemoteException ex) {
968             throw ex.rethrowFromSystemServer();
969         }
970     }
971 
972 
973     /**
974      * Sets the brightness of the display.
975      *
976      * @param brightness The brightness value from 0.0f to 1.0f.
977      *
978      * @hide
979      */
setBrightness(int displayId, float brightness)980     public void setBrightness(int displayId, float brightness) {
981         try {
982             mDm.setBrightness(displayId, brightness);
983         } catch (RemoteException ex) {
984             throw ex.rethrowFromSystemServer();
985         }
986     }
987 
988     /**
989      * Report whether/how the display supports DISPLAY_DECORATION.
990      *
991      * @param displayId The display whose support is being queried.
992      *
993      * @hide
994      */
getDisplayDecorationSupport(int displayId)995     public DisplayDecorationSupport getDisplayDecorationSupport(int displayId) {
996         try {
997             return mDm.getDisplayDecorationSupport(displayId);
998         } catch (RemoteException ex) {
999             throw ex.rethrowFromSystemServer();
1000         }
1001     }
1002 
1003     /**
1004      * Gets the brightness of the display.
1005      *
1006      * @param displayId The display from which to get the brightness
1007      *
1008      * @hide
1009      */
getBrightness(int displayId)1010     public float getBrightness(int displayId) {
1011         try {
1012             return mDm.getBrightness(displayId);
1013         } catch (RemoteException ex) {
1014             throw ex.rethrowFromSystemServer();
1015         }
1016     }
1017 
1018     /**
1019      * Temporarily sets the auto brightness adjustment factor.
1020      * <p>
1021      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
1022      * </p>
1023      *
1024      * @param adjustment The adjustment factor from -1.0 to 1.0.
1025      *
1026      * @hide Requires signature permission.
1027      */
setTemporaryAutoBrightnessAdjustment(float adjustment)1028     public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
1029         try {
1030             mDm.setTemporaryAutoBrightnessAdjustment(adjustment);
1031         } catch (RemoteException ex) {
1032             throw ex.rethrowFromSystemServer();
1033         }
1034     }
1035 
1036     /**
1037      * Returns the minimum brightness curve, which guarantess that any brightness curve that dips
1038      * below it is rejected by the system.
1039      * This prevent auto-brightness from setting the screen so dark as to prevent the user from
1040      * resetting or disabling it, and maps lux to the absolute minimum nits that are still readable
1041      * in that ambient brightness.
1042      *
1043      * @return The minimum brightness curve (as lux values and their corresponding nits values).
1044      */
getMinimumBrightnessCurve()1045     public Pair<float[], float[]> getMinimumBrightnessCurve() {
1046         try {
1047             Curve curve = mDm.getMinimumBrightnessCurve();
1048             return Pair.create(curve.getX(), curve.getY());
1049         } catch (RemoteException ex) {
1050             throw ex.rethrowFromSystemServer();
1051         }
1052     }
1053 
1054     /**
1055      * Retrieves ambient brightness stats.
1056      */
getAmbientBrightnessStats()1057     public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
1058         try {
1059             ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
1060             if (stats == null) {
1061                 return Collections.emptyList();
1062             }
1063             return stats.getList();
1064         } catch (RemoteException ex) {
1065             throw ex.rethrowFromSystemServer();
1066         }
1067     }
1068 
1069     /**
1070      * Sets the default display mode, according to the refresh rate and the resolution chosen by the
1071      * user.
1072      */
setUserPreferredDisplayMode(int displayId, Display.Mode mode)1073     public void setUserPreferredDisplayMode(int displayId, Display.Mode mode) {
1074         try {
1075             mDm.setUserPreferredDisplayMode(displayId, mode);
1076         } catch (RemoteException ex) {
1077             throw ex.rethrowFromSystemServer();
1078         }
1079     }
1080 
1081     /**
1082      * Returns the user preferred display mode.
1083      */
getUserPreferredDisplayMode(int displayId)1084     public Display.Mode getUserPreferredDisplayMode(int displayId) {
1085         try {
1086             return mDm.getUserPreferredDisplayMode(displayId);
1087         } catch (RemoteException ex) {
1088             throw ex.rethrowFromSystemServer();
1089         }
1090     }
1091 
1092     /**
1093      * Returns the system preferred display mode.
1094      */
getSystemPreferredDisplayMode(int displayId)1095     public Display.Mode getSystemPreferredDisplayMode(int displayId) {
1096         try {
1097             return mDm.getSystemPreferredDisplayMode(displayId);
1098         } catch (RemoteException ex) {
1099             throw ex.rethrowFromSystemServer();
1100         }
1101     }
1102 
1103     /**
1104      * Sets the {@link HdrConversionMode} for the device.
1105      */
setHdrConversionMode(@onNull HdrConversionMode hdrConversionMode)1106     public void setHdrConversionMode(@NonNull HdrConversionMode hdrConversionMode) {
1107         try {
1108             mDm.setHdrConversionMode(hdrConversionMode);
1109         } catch (RemoteException ex) {
1110             throw ex.rethrowFromSystemServer();
1111         }
1112     }
1113 
1114     /**
1115      * Returns the {@link HdrConversionMode} of the device, which is set by the user.
1116      * The HDR conversion mode chosen by user is returned irrespective of whether HDR conversion
1117      * is disabled by an app.
1118      */
getHdrConversionModeSetting()1119     public HdrConversionMode getHdrConversionModeSetting() {
1120         try {
1121             return mDm.getHdrConversionModeSetting();
1122         } catch (RemoteException ex) {
1123             throw ex.rethrowFromSystemServer();
1124         }
1125     }
1126 
1127     /**
1128      * Returns the {@link HdrConversionMode} of the device.
1129      */
getHdrConversionMode()1130     public HdrConversionMode getHdrConversionMode() {
1131         try {
1132             return mDm.getHdrConversionMode();
1133         } catch (RemoteException ex) {
1134             throw ex.rethrowFromSystemServer();
1135         }
1136     }
1137 
1138     /**
1139      * Returns the HDR output types supported by the device.
1140      */
getSupportedHdrOutputTypes()1141     public @HdrType int[] getSupportedHdrOutputTypes() {
1142         try {
1143             return mDm.getSupportedHdrOutputTypes();
1144         } catch (RemoteException ex) {
1145             throw ex.rethrowFromSystemServer();
1146         }
1147     }
1148 
1149     /**
1150      * When enabled the app requested display resolution and refresh rate is always selected
1151      * in DisplayModeDirector regardless of user settings and policies for low brightness, low
1152      * battery etc.
1153      */
setShouldAlwaysRespectAppRequestedMode(boolean enabled)1154     public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
1155         try {
1156             mDm.setShouldAlwaysRespectAppRequestedMode(enabled);
1157         } catch (RemoteException ex) {
1158             throw ex.rethrowFromSystemServer();
1159         }
1160     }
1161 
1162     /**
1163      * Returns whether DisplayModeDirector is running in a mode which always selects the app
1164      * requested display mode and ignores user settings and policies for low brightness, low
1165      * battery etc.
1166      */
shouldAlwaysRespectAppRequestedMode()1167     public boolean shouldAlwaysRespectAppRequestedMode() {
1168         try {
1169             return mDm.shouldAlwaysRespectAppRequestedMode();
1170         } catch (RemoteException ex) {
1171             throw ex.rethrowFromSystemServer();
1172         }
1173     }
1174 
1175     /**
1176      * Sets the refresh rate switching type.
1177      *
1178      * @hide
1179      */
setRefreshRateSwitchingType(@isplayManager.SwitchingType int newValue)1180     public void setRefreshRateSwitchingType(@DisplayManager.SwitchingType int newValue) {
1181         try {
1182             mDm.setRefreshRateSwitchingType(newValue);
1183         } catch (RemoteException ex) {
1184             throw ex.rethrowFromSystemServer();
1185         }
1186     }
1187 
1188     /**
1189      * Returns the refresh rate switching type.
1190      *
1191      * @hide
1192      */
1193     @DisplayManager.SwitchingType
getRefreshRateSwitchingType()1194     public int getRefreshRateSwitchingType() {
1195         try {
1196             return mDm.getRefreshRateSwitchingType();
1197         } catch (RemoteException ex) {
1198             throw ex.rethrowFromSystemServer();
1199         }
1200     }
1201 
1202     /**
1203      * Sets allowed display mode ids
1204      *
1205      * @hide
1206      */
1207     @RequiresPermission("android.permission.RESTRICT_DISPLAY_MODES")
requestDisplayModes(int displayId, @Nullable int[] modeIds)1208     public void requestDisplayModes(int displayId, @Nullable int[] modeIds) {
1209         try {
1210             mDm.requestDisplayModes(mToken, displayId, modeIds);
1211         } catch (RemoteException ex) {
1212             throw ex.rethrowFromSystemServer();
1213         }
1214     }
1215 
1216     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
1217         @Override
onDisplayEvent(int displayId, @DisplayEvent int event)1218         public void onDisplayEvent(int displayId, @DisplayEvent int event) {
1219             if (DEBUG) {
1220                 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + eventToString(
1221                         event));
1222             }
1223             handleDisplayEvent(displayId, event, false /* forceUpdate */);
1224         }
1225     }
1226 
1227     private static final class DisplayListenerDelegate {
1228         public final DisplayListener mListener;
1229         public volatile long mEventsMask;
1230 
1231         private final DisplayInfo mDisplayInfo = new DisplayInfo();
1232         private final Executor mExecutor;
1233         private AtomicLong mGenerationId = new AtomicLong(1);
1234         private final String mPackageName;
1235 
DisplayListenerDelegate(DisplayListener listener, @NonNull Executor executor, @EventsMask long eventsMask, String packageName)1236         DisplayListenerDelegate(DisplayListener listener, @NonNull Executor executor,
1237                 @EventsMask long eventsMask, String packageName) {
1238             mExecutor = executor;
1239             mListener = listener;
1240             mEventsMask = eventsMask;
1241             mPackageName = packageName;
1242         }
1243 
sendDisplayEvent(int displayId, @DisplayEvent int event, @Nullable DisplayInfo info, boolean forceUpdate)1244         void sendDisplayEvent(int displayId, @DisplayEvent int event, @Nullable DisplayInfo info,
1245                 boolean forceUpdate) {
1246             if (extraLogging()) {
1247                 Slog.i(TAG, "Sending Display Event: " + eventToString(event));
1248             }
1249             long generationId = mGenerationId.get();
1250             mExecutor.execute(() -> {
1251                 // If the generation id's don't match we were canceled
1252                 if (generationId == mGenerationId.get()) {
1253                     handleDisplayEventInner(displayId, event, info, forceUpdate);
1254                 }
1255             });
1256         }
1257 
clearEvents()1258         void clearEvents() {
1259             mGenerationId.incrementAndGet();
1260         }
1261 
setEventsMask(@ventsMask long newEventsMask)1262         void setEventsMask(@EventsMask long newEventsMask) {
1263             mEventsMask = newEventsMask;
1264         }
1265 
handleDisplayEventInner(int displayId, @DisplayEvent int event, @Nullable DisplayInfo info, boolean forceUpdate)1266         private void handleDisplayEventInner(int displayId, @DisplayEvent int event,
1267                 @Nullable DisplayInfo info, boolean forceUpdate) {
1268             if (extraLogging()) {
1269                 Slog.i(TAG, "DLD(" + eventToString(event)
1270                         + ", display=" + displayId
1271                         + ", mEventsMask=" + Long.toBinaryString(mEventsMask)
1272                         + ", mPackageName=" + mPackageName
1273                         + ", displayInfo=" + info
1274                         + ", listener=" + mListener.getClass() + ")");
1275             }
1276             if (DEBUG) {
1277                 Trace.beginSection(
1278                         TextUtils.trimToSize(
1279                                 "DLD(" + eventToString(event)
1280                                 + ", display=" + displayId
1281                                 + ", listener=" + mListener.getClass() + ")", 127));
1282             }
1283             switch (event) {
1284                 case EVENT_DISPLAY_ADDED:
1285                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
1286                         mListener.onDisplayAdded(displayId);
1287                     }
1288                     break;
1289                 case EVENT_DISPLAY_CHANGED:
1290                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
1291                         if (info != null && (forceUpdate || !info.equals(mDisplayInfo))) {
1292                             if (extraLogging()) {
1293                                 Slog.i(TAG, "Sending onDisplayChanged: Display Changed. Info: "
1294                                         + info);
1295                             }
1296                             mDisplayInfo.copyFrom(info);
1297                             mListener.onDisplayChanged(displayId);
1298                         }
1299                     }
1300                     break;
1301                 case EVENT_DISPLAY_BRIGHTNESS_CHANGED:
1302                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0) {
1303                         mListener.onDisplayChanged(displayId);
1304                     }
1305                     break;
1306                 case EVENT_DISPLAY_REMOVED:
1307                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) {
1308                         mListener.onDisplayRemoved(displayId);
1309                     }
1310                     break;
1311                 case EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
1312                     if ((mEventsMask & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0) {
1313                         mListener.onDisplayChanged(displayId);
1314                     }
1315                     break;
1316                 case EVENT_DISPLAY_CONNECTED:
1317                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
1318                         mListener.onDisplayConnected(displayId);
1319                     }
1320                     break;
1321                 case EVENT_DISPLAY_DISCONNECTED:
1322                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
1323                         mListener.onDisplayDisconnected(displayId);
1324                     }
1325                     break;
1326             }
1327             if (DEBUG) {
1328                 Trace.endSection();
1329             }
1330         }
1331 
1332         @Override
toString()1333         public String toString() {
1334             return "mask: {" + mEventsMask + "}, for " + mListener.getClass();
1335         }
1336     }
1337 
1338     /**
1339      * Assists in dispatching VirtualDisplay lifecycle event callbacks on a given Executor.
1340      */
1341     public static final class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
1342         @Nullable private final VirtualDisplay.Callback mCallback;
1343         @Nullable private final Executor mExecutor;
1344 
1345         /**
1346          * Creates a virtual display callback.
1347          *
1348          * @param callback The callback to call for virtual display events, or {@code null} if the
1349          * caller does not wish to receive callback events.
1350          * @param executor The executor to call the {@code callback} on. Must not be {@code null} if
1351          * the callback is not {@code null}.
1352          */
VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor)1353         public VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) {
1354             mCallback = callback;
1355             mExecutor = mCallback != null ? Objects.requireNonNull(executor) : null;
1356         }
1357 
1358         // These methods are called from the binder thread, but the AIDL is oneway, so it should be
1359         // safe to call the callback on arbitrary executors directly without risking blocking
1360         // the system.
1361 
1362         @Override // Binder call
onPaused()1363         public void onPaused() {
1364             if (mCallback != null) {
1365                 mExecutor.execute(mCallback::onPaused);
1366             }
1367         }
1368 
1369         @Override // Binder call
onResumed()1370         public void onResumed() {
1371             if (mCallback != null) {
1372                 mExecutor.execute(mCallback::onResumed);
1373             }
1374         }
1375 
1376         @Override // Binder call
onStopped()1377         public void onStopped() {
1378             if (mCallback != null) {
1379                 mExecutor.execute(mCallback::onStopped);
1380             }
1381         }
1382     }
1383 
1384     /**
1385      * Name of the property containing a unique token which changes every time we update the
1386      * system's display configuration.
1387      */
1388     public static final String CACHE_KEY_DISPLAY_INFO_PROPERTY =
1389             "cache_key.display_info";
1390 
1391     /**
1392      * Invalidates the contents of the display info cache for all applications. Can only
1393      * be called by system_server.
1394      */
invalidateLocalDisplayInfoCaches()1395     public static void invalidateLocalDisplayInfoCaches() {
1396         PropertyInvalidatedCache.invalidateCache(CACHE_KEY_DISPLAY_INFO_PROPERTY);
1397     }
1398 
1399     /**
1400      * Disables the binder call cache.
1401      */
disableLocalDisplayInfoCaches()1402     public void disableLocalDisplayInfoCaches() {
1403         mDisplayCache = null;
1404     }
1405 
nSignalNativeCallbacks(float refreshRate)1406     private static native void nSignalNativeCallbacks(float refreshRate);
1407 
1408     /**
1409      * Called from AChoreographer via JNI.
1410      * Registers AChoreographer so that refresh rate callbacks can be dispatched from DMS.
1411      * Public for unit testing to be able to call this method.
1412      */
1413     @VisibleForTesting
registerNativeChoreographerForRefreshRateCallbacks()1414     public void registerNativeChoreographerForRefreshRateCallbacks() {
1415         synchronized (mLock) {
1416             mDispatchNativeCallbacks = true;
1417             registerCallbackIfNeededLocked();
1418             updateCallbackIfNeededLocked();
1419             DisplayInfo display = getDisplayInfoLocked(Display.DEFAULT_DISPLAY);
1420             if (display != null) {
1421                 // We need to tell AChoreographer instances the current refresh rate so that apps
1422                 // can get it for free once a callback first registers.
1423                 mNativeCallbackReportedRefreshRate = display.getRefreshRate();
1424                 nSignalNativeCallbacks(mNativeCallbackReportedRefreshRate);
1425             }
1426         }
1427     }
1428 
1429     /**
1430      * Called from AChoreographer via JNI.
1431      * Unregisters AChoreographer from receiving refresh rate callbacks.
1432      * Public for unit testing to be able to call this method.
1433      */
1434     @VisibleForTesting
unregisterNativeChoreographerForRefreshRateCallbacks()1435     public void unregisterNativeChoreographerForRefreshRateCallbacks() {
1436         synchronized (mLock) {
1437             mDispatchNativeCallbacks = false;
1438             updateCallbackIfNeededLocked();
1439         }
1440     }
1441 
eventToString(@isplayEvent int event)1442     private static String eventToString(@DisplayEvent int event) {
1443         switch (event) {
1444             case EVENT_DISPLAY_ADDED:
1445                 return "ADDED";
1446             case EVENT_DISPLAY_CHANGED:
1447                 return "CHANGED";
1448             case EVENT_DISPLAY_REMOVED:
1449                 return "REMOVED";
1450             case EVENT_DISPLAY_BRIGHTNESS_CHANGED:
1451                 return "BRIGHTNESS_CHANGED";
1452             case EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
1453                 return "HDR_SDR_RATIO_CHANGED";
1454             case EVENT_DISPLAY_CONNECTED:
1455                 return "EVENT_DISPLAY_CONNECTED";
1456             case EVENT_DISPLAY_DISCONNECTED:
1457                 return "EVENT_DISPLAY_DISCONNECTED";
1458         }
1459         return "UNKNOWN";
1460     }
1461 
1462 
initExtraLogging()1463     private static boolean initExtraLogging() {
1464         if (sCurrentPackageName == null) {
1465             sCurrentPackageName = ActivityThread.currentPackageName();
1466             sExtraDisplayListenerLogging = !TextUtils.isEmpty(EXTRA_LOGGING_PACKAGE_NAME)
1467                     && EXTRA_LOGGING_PACKAGE_NAME.equals(sCurrentPackageName);
1468         }
1469         return sExtraDisplayListenerLogging;
1470     }
1471 
extraLogging()1472     private static boolean extraLogging() {
1473         return sExtraDisplayListenerLogging;
1474     }
1475 }
1476