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