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 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.PropertyInvalidatedCache;
22 import android.compat.annotation.UnsupportedAppUsage;
23 import android.content.Context;
24 import android.content.pm.ParceledListSlice;
25 import android.content.res.Resources;
26 import android.graphics.ColorSpace;
27 import android.graphics.Point;
28 import android.hardware.display.DisplayManager.DisplayListener;
29 import android.media.projection.IMediaProjection;
30 import android.media.projection.MediaProjection;
31 import android.os.Handler;
32 import android.os.IBinder;
33 import android.os.Looper;
34 import android.os.Message;
35 import android.os.RemoteException;
36 import android.os.ServiceManager;
37 import android.util.Log;
38 import android.util.Pair;
39 import android.util.SparseArray;
40 import android.view.Display;
41 import android.view.DisplayAdjustments;
42 import android.view.DisplayInfo;
43 import android.view.Surface;
44 
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.List;
48 
49 /**
50  * Manager communication with the display manager service on behalf of
51  * an application process.  You're probably looking for {@link DisplayManager}.
52  *
53  * @hide
54  */
55 public final class DisplayManagerGlobal {
56     private static final String TAG = "DisplayManager";
57     private static final boolean DEBUG = false;
58 
59     // True if display info and display ids should be cached.
60     //
61     // FIXME: The cache is currently disabled because it's unclear whether we have the
62     // necessary guarantees that the caches will always be flushed before clients
63     // attempt to observe their new state.  For example, depending on the order
64     // in which the binder transactions take place, we might have a problem where
65     // an application could start processing a configuration change due to a display
66     // orientation change before the display info cache has actually been invalidated.
67     private static final boolean USE_CACHE = false;
68 
69     public static final int EVENT_DISPLAY_ADDED = 1;
70     public static final int EVENT_DISPLAY_CHANGED = 2;
71     public static final int EVENT_DISPLAY_REMOVED = 3;
72 
73     @UnsupportedAppUsage
74     private static DisplayManagerGlobal sInstance;
75 
76     // Guarded by mLock
77     private boolean mDispatchNativeCallbacks = false;
78     private final Object mLock = new Object();
79 
80     @UnsupportedAppUsage
81     private final IDisplayManager mDm;
82 
83     private DisplayManagerCallback mCallback;
84     private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
85             new ArrayList<DisplayListenerDelegate>();
86 
87     private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>();
88     private final ColorSpace mWideColorSpace;
89     private int[] mDisplayIdCache;
90 
91     private int mWifiDisplayScanNestCount;
92 
DisplayManagerGlobal(IDisplayManager dm)93     private DisplayManagerGlobal(IDisplayManager dm) {
94         mDm = dm;
95         try {
96             mWideColorSpace =
97                     ColorSpace.get(
98                             ColorSpace.Named.values()[mDm.getPreferredWideGamutColorSpaceId()]);
99         } catch (RemoteException ex) {
100             throw ex.rethrowFromSystemServer();
101         }
102     }
103 
104     private PropertyInvalidatedCache<Integer, DisplayInfo> mDisplayCache =
105             new PropertyInvalidatedCache<Integer, DisplayInfo>(
106                 8, // size of display cache
107                 CACHE_KEY_DISPLAY_INFO_PROPERTY) {
108                 @Override
109                 protected DisplayInfo recompute(Integer id) {
110                     try {
111                         return mDm.getDisplayInfo(id);
112                     } catch (RemoteException ex) {
113                         throw ex.rethrowFromSystemServer();
114                     }
115                 }
116             };
117 
118     /**
119      * Gets an instance of the display manager global singleton.
120      *
121      * @return The display manager instance, may be null early in system startup
122      * before the display manager has been fully initialized.
123      */
124     @UnsupportedAppUsage
getInstance()125     public static DisplayManagerGlobal getInstance() {
126         synchronized (DisplayManagerGlobal.class) {
127             if (sInstance == null) {
128                 IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
129                 if (b != null) {
130                     sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
131                 }
132             }
133             return sInstance;
134         }
135     }
136 
137     /**
138      * Get information about a particular logical display.
139      *
140      * @param displayId The logical display id.
141      * @return Information about the specified display, or null if it does not exist.
142      * This object belongs to an internal cache and should be treated as if it were immutable.
143      */
144     @UnsupportedAppUsage
getDisplayInfo(int displayId)145     public DisplayInfo getDisplayInfo(int displayId) {
146         synchronized (mLock) {
147             return getDisplayInfoLocked(displayId);
148         }
149     }
150 
151     /**
152      * Gets information about a particular logical display
153      * See {@link getDisplayInfo}, but assumes that {@link mLock} is held
154      */
getDisplayInfoLocked(int displayId)155     private @Nullable DisplayInfo getDisplayInfoLocked(int displayId) {
156         DisplayInfo info = null;
157         if (mDisplayCache != null) {
158             info = mDisplayCache.query(displayId);
159         } else {
160             try {
161                 info = mDm.getDisplayInfo(displayId);
162             } catch (RemoteException ex) {
163                 ex.rethrowFromSystemServer();
164             }
165         }
166         if (info == null) {
167             return null;
168         }
169 
170         registerCallbackIfNeededLocked();
171 
172         if (DEBUG) {
173             Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
174         }
175         return info;
176     }
177 
178     /**
179      * Gets all currently valid logical display ids.
180      *
181      * @return An array containing all display ids.
182      */
183     @UnsupportedAppUsage
getDisplayIds()184     public int[] getDisplayIds() {
185         try {
186             synchronized (mLock) {
187                 if (USE_CACHE) {
188                     if (mDisplayIdCache != null) {
189                         return mDisplayIdCache;
190                     }
191                 }
192 
193                 int[] displayIds = mDm.getDisplayIds();
194                 if (USE_CACHE) {
195                     mDisplayIdCache = displayIds;
196                 }
197                 registerCallbackIfNeededLocked();
198                 return displayIds;
199             }
200         } catch (RemoteException ex) {
201             throw ex.rethrowFromSystemServer();
202         }
203     }
204 
205     /**
206      * Check if specified UID's content is present on display and should be granted access to it.
207      *
208      * @param uid UID to be checked.
209      * @param displayId id of the display where presence of the content is checked.
210      * @return {@code true} if UID is present on display, {@code false} otherwise.
211      */
isUidPresentOnDisplay(int uid, int displayId)212     public boolean isUidPresentOnDisplay(int uid, int displayId) {
213         try {
214             return mDm.isUidPresentOnDisplay(uid, displayId);
215         } catch (RemoteException ex) {
216             throw ex.rethrowFromSystemServer();
217         }
218     }
219 
220     /**
221      * Gets information about a logical display.
222      *
223      * The display metrics may be adjusted to provide compatibility
224      * for legacy applications or limited screen areas.
225      *
226      * @param displayId The logical display id.
227      * @param daj The compatibility info and activityToken.
228      * @return The display object, or null if there is no display with the given id.
229      */
getCompatibleDisplay(int displayId, DisplayAdjustments daj)230     public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
231         DisplayInfo displayInfo = getDisplayInfo(displayId);
232         if (displayInfo == null) {
233             return null;
234         }
235         return new Display(this, displayId, displayInfo, daj);
236     }
237 
238     /**
239      * Gets information about a logical display.
240      *
241      * The display metrics may be adjusted to provide compatibility
242      * for legacy applications or limited screen areas.
243      *
244      * @param displayId The logical display id.
245      * @param resources Resources providing compatibility info.
246      * @return The display object, or null if there is no display with the given id.
247      */
getCompatibleDisplay(int displayId, Resources resources)248     public Display getCompatibleDisplay(int displayId, Resources resources) {
249         DisplayInfo displayInfo = getDisplayInfo(displayId);
250         if (displayInfo == null) {
251             return null;
252         }
253         return new Display(this, displayId, displayInfo, resources);
254     }
255 
256     /**
257      * Gets information about a logical display without applying any compatibility metrics.
258      *
259      * @param displayId The logical display id.
260      * @return The display object, or null if there is no display with the given id.
261      */
262     @UnsupportedAppUsage
getRealDisplay(int displayId)263     public Display getRealDisplay(int displayId) {
264         return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
265     }
266 
267     /**
268      * Register a listener for display-related changes.
269      *
270      * @param listener The listener that will be called when display changes occur.
271      * @param handler Handler for the thread that will be receiving the callbacks. May be null.
272      * If null, listener will use the handler for the current thread, and if still null,
273      * the handler for the main thread.
274      * If that is still null, a runtime exception will be thrown.
275      */
registerDisplayListener(@onNull DisplayListener listener, @Nullable Handler handler)276     public void registerDisplayListener(@NonNull DisplayListener listener,
277             @Nullable Handler handler) {
278         if (listener == null) {
279             throw new IllegalArgumentException("listener must not be null");
280         }
281 
282         synchronized (mLock) {
283             int index = findDisplayListenerLocked(listener);
284             if (index < 0) {
285                 Looper looper = getLooperForHandler(handler);
286                 mDisplayListeners.add(new DisplayListenerDelegate(listener, looper));
287                 registerCallbackIfNeededLocked();
288             }
289         }
290     }
291 
unregisterDisplayListener(DisplayListener listener)292     public void unregisterDisplayListener(DisplayListener listener) {
293         if (listener == null) {
294             throw new IllegalArgumentException("listener must not be null");
295         }
296 
297         synchronized (mLock) {
298             int index = findDisplayListenerLocked(listener);
299             if (index >= 0) {
300                 DisplayListenerDelegate d = mDisplayListeners.get(index);
301                 d.clearEvents();
302                 mDisplayListeners.remove(index);
303             }
304         }
305     }
306 
getLooperForHandler(@ullable Handler handler)307     private static Looper getLooperForHandler(@Nullable Handler handler) {
308         Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
309         if (looper == null) {
310             looper = Looper.getMainLooper();
311         }
312         if (looper == null) {
313             throw new RuntimeException("Could not get Looper for the UI thread.");
314         }
315         return looper;
316     }
317 
findDisplayListenerLocked(DisplayListener listener)318     private int findDisplayListenerLocked(DisplayListener listener) {
319         final int numListeners = mDisplayListeners.size();
320         for (int i = 0; i < numListeners; i++) {
321             if (mDisplayListeners.get(i).mListener == listener) {
322                 return i;
323             }
324         }
325         return -1;
326     }
327 
registerCallbackIfNeededLocked()328     private void registerCallbackIfNeededLocked() {
329         if (mCallback == null) {
330             mCallback = new DisplayManagerCallback();
331             try {
332                 mDm.registerCallback(mCallback);
333             } catch (RemoteException ex) {
334                 throw ex.rethrowFromSystemServer();
335             }
336         }
337     }
338 
handleDisplayEvent(int displayId, int event)339     private void handleDisplayEvent(int displayId, int event) {
340         synchronized (mLock) {
341             if (USE_CACHE) {
342                 mDisplayInfoCache.remove(displayId);
343 
344                 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
345                     mDisplayIdCache = null;
346                 }
347             }
348 
349             final int numListeners = mDisplayListeners.size();
350             for (int i = 0; i < numListeners; i++) {
351                 mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
352             }
353             if (event == EVENT_DISPLAY_CHANGED && mDispatchNativeCallbacks) {
354                 // Choreographer only supports a single display, so only dispatch refresh rate
355                 // changes for the default display.
356                 if (displayId == Display.DEFAULT_DISPLAY) {
357                     // We can likely save a binder hop if we attach the refresh rate onto the
358                     // listener.
359                     DisplayInfo display = getDisplayInfoLocked(displayId);
360                     if (display != null) {
361                         float refreshRate = display.getMode().getRefreshRate();
362                         // Signal native callbacks if we ever set a refresh rate.
363                         nSignalNativeCallbacks(refreshRate);
364                     }
365                 }
366             }
367         }
368     }
369 
startWifiDisplayScan()370     public void startWifiDisplayScan() {
371         synchronized (mLock) {
372             if (mWifiDisplayScanNestCount++ == 0) {
373                 registerCallbackIfNeededLocked();
374                 try {
375                     mDm.startWifiDisplayScan();
376                 } catch (RemoteException ex) {
377                     throw ex.rethrowFromSystemServer();
378                 }
379             }
380         }
381     }
382 
stopWifiDisplayScan()383     public void stopWifiDisplayScan() {
384         synchronized (mLock) {
385             if (--mWifiDisplayScanNestCount == 0) {
386                 try {
387                     mDm.stopWifiDisplayScan();
388                 } catch (RemoteException ex) {
389                     throw ex.rethrowFromSystemServer();
390                 }
391             } else if (mWifiDisplayScanNestCount < 0) {
392                 Log.wtf(TAG, "Wifi display scan nest count became negative: "
393                         + mWifiDisplayScanNestCount);
394                 mWifiDisplayScanNestCount = 0;
395             }
396         }
397     }
398 
connectWifiDisplay(String deviceAddress)399     public void connectWifiDisplay(String deviceAddress) {
400         if (deviceAddress == null) {
401             throw new IllegalArgumentException("deviceAddress must not be null");
402         }
403 
404         try {
405             mDm.connectWifiDisplay(deviceAddress);
406         } catch (RemoteException ex) {
407             throw ex.rethrowFromSystemServer();
408         }
409     }
410 
pauseWifiDisplay()411     public void pauseWifiDisplay() {
412         try {
413             mDm.pauseWifiDisplay();
414         } catch (RemoteException ex) {
415             throw ex.rethrowFromSystemServer();
416         }
417     }
418 
resumeWifiDisplay()419     public void resumeWifiDisplay() {
420         try {
421             mDm.resumeWifiDisplay();
422         } catch (RemoteException ex) {
423             throw ex.rethrowFromSystemServer();
424         }
425     }
426 
427     @UnsupportedAppUsage
disconnectWifiDisplay()428     public void disconnectWifiDisplay() {
429         try {
430             mDm.disconnectWifiDisplay();
431         } catch (RemoteException ex) {
432             throw ex.rethrowFromSystemServer();
433         }
434     }
435 
renameWifiDisplay(String deviceAddress, String alias)436     public void renameWifiDisplay(String deviceAddress, String alias) {
437         if (deviceAddress == null) {
438             throw new IllegalArgumentException("deviceAddress must not be null");
439         }
440 
441         try {
442             mDm.renameWifiDisplay(deviceAddress, alias);
443         } catch (RemoteException ex) {
444             throw ex.rethrowFromSystemServer();
445         }
446     }
447 
forgetWifiDisplay(String deviceAddress)448     public void forgetWifiDisplay(String deviceAddress) {
449         if (deviceAddress == null) {
450             throw new IllegalArgumentException("deviceAddress must not be null");
451         }
452 
453         try {
454             mDm.forgetWifiDisplay(deviceAddress);
455         } catch (RemoteException ex) {
456             throw ex.rethrowFromSystemServer();
457         }
458     }
459 
460     @UnsupportedAppUsage
getWifiDisplayStatus()461     public WifiDisplayStatus getWifiDisplayStatus() {
462         try {
463             return mDm.getWifiDisplayStatus();
464         } catch (RemoteException ex) {
465             throw ex.rethrowFromSystemServer();
466         }
467     }
468 
requestColorMode(int displayId, int colorMode)469     public void requestColorMode(int displayId, int colorMode) {
470         try {
471             mDm.requestColorMode(displayId, colorMode);
472         } catch (RemoteException ex) {
473             throw ex.rethrowFromSystemServer();
474         }
475     }
476 
createVirtualDisplay(@onNull Context context, MediaProjection projection, @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback, Handler handler)477     public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection,
478             @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback,
479             Handler handler) {
480         VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
481         IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
482         int displayId;
483         try {
484             displayId = mDm.createVirtualDisplay(virtualDisplayConfig, callbackWrapper,
485                     projectionToken, context.getPackageName());
486         } catch (RemoteException ex) {
487             throw ex.rethrowFromSystemServer();
488         }
489         if (displayId < 0) {
490             Log.e(TAG, "Could not create virtual display: " + virtualDisplayConfig.getName());
491             return null;
492         }
493         Display display = getRealDisplay(displayId);
494         if (display == null) {
495             Log.wtf(TAG, "Could not obtain display info for newly created "
496                     + "virtual display: " + virtualDisplayConfig.getName());
497             try {
498                 mDm.releaseVirtualDisplay(callbackWrapper);
499             } catch (RemoteException ex) {
500                 throw ex.rethrowFromSystemServer();
501             }
502             return null;
503         }
504         return new VirtualDisplay(this, display, callbackWrapper,
505                 virtualDisplayConfig.getSurface());
506     }
507 
setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface)508     public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
509         try {
510             mDm.setVirtualDisplaySurface(token, surface);
511             setVirtualDisplayState(token, surface != null);
512         } catch (RemoteException ex) {
513             throw ex.rethrowFromSystemServer();
514         }
515     }
516 
resizeVirtualDisplay(IVirtualDisplayCallback token, int width, int height, int densityDpi)517     public void resizeVirtualDisplay(IVirtualDisplayCallback token,
518             int width, int height, int densityDpi) {
519         try {
520             mDm.resizeVirtualDisplay(token, width, height, densityDpi);
521         } catch (RemoteException ex) {
522             throw ex.rethrowFromSystemServer();
523         }
524     }
525 
releaseVirtualDisplay(IVirtualDisplayCallback token)526     public void releaseVirtualDisplay(IVirtualDisplayCallback token) {
527         try {
528             mDm.releaseVirtualDisplay(token);
529         } catch (RemoteException ex) {
530             throw ex.rethrowFromSystemServer();
531         }
532     }
533 
setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn)534     void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) {
535         try {
536             mDm.setVirtualDisplayState(token, isOn);
537         } catch (RemoteException ex) {
538             throw ex.rethrowFromSystemServer();
539         }
540     }
541 
542     /**
543      * Gets the stable device display size, in pixels.
544      */
getStableDisplaySize()545     public Point getStableDisplaySize() {
546         try {
547             return mDm.getStableDisplaySize();
548         } catch (RemoteException ex) {
549             throw ex.rethrowFromSystemServer();
550         }
551     }
552 
553     /**
554      * Retrieves brightness change events.
555      */
getBrightnessEvents(String callingPackage)556     public List<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
557         try {
558             ParceledListSlice<BrightnessChangeEvent> events =
559                     mDm.getBrightnessEvents(callingPackage);
560             if (events == null) {
561                 return Collections.emptyList();
562             }
563             return events.getList();
564         } catch (RemoteException ex) {
565             throw ex.rethrowFromSystemServer();
566         }
567     }
568 
569     /**
570      * Gets the preferred wide gamut color space for all displays.
571      * The wide gamut color space is returned from composition pipeline
572      * based on hardware capability.
573      *
574      * @hide
575      */
getPreferredWideGamutColorSpace()576     public ColorSpace getPreferredWideGamutColorSpace() {
577         return mWideColorSpace;
578     }
579 
580     /**
581      * Sets the global brightness configuration for a given user.
582      *
583      * @hide
584      */
setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId, String packageName)585     public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId,
586             String packageName) {
587         try {
588             mDm.setBrightnessConfigurationForUser(c, userId, packageName);
589         } catch (RemoteException ex) {
590             throw ex.rethrowFromSystemServer();
591         }
592     }
593 
594     /**
595      * Gets the global brightness configuration for a given user or null if one hasn't been set.
596      *
597      * @hide
598      */
getBrightnessConfigurationForUser(int userId)599     public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
600         try {
601             return mDm.getBrightnessConfigurationForUser(userId);
602         } catch (RemoteException ex) {
603             throw ex.rethrowFromSystemServer();
604         }
605     }
606 
607     /**
608      * Gets the default brightness configuration or null if one hasn't been configured.
609      *
610      * @hide
611      */
getDefaultBrightnessConfiguration()612     public BrightnessConfiguration getDefaultBrightnessConfiguration() {
613         try {
614             return mDm.getDefaultBrightnessConfiguration();
615         } catch (RemoteException ex) {
616             throw ex.rethrowFromSystemServer();
617         }
618     }
619 
620     /**
621      * Gets the last requested minimal post processing setting for the display with displayId.
622      *
623      * @hide
624      */
isMinimalPostProcessingRequested(int displayId)625     public boolean isMinimalPostProcessingRequested(int displayId) {
626         try {
627             return mDm.isMinimalPostProcessingRequested(displayId);
628         } catch (RemoteException ex) {
629             throw ex.rethrowFromSystemServer();
630         }
631     }
632 
633     /**
634      * Temporarily sets the brightness of the display.
635      * <p>
636      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
637      * </p>
638      *
639      * @param brightness The brightness value from 0 to 255.
640      *
641      * @hide Requires signature permission.
642      */
setTemporaryBrightness(float brightness)643     public void setTemporaryBrightness(float brightness) {
644         try {
645             mDm.setTemporaryBrightness(brightness);
646         } catch (RemoteException ex) {
647             throw ex.rethrowFromSystemServer();
648         }
649     }
650 
651     /**
652      * Temporarily sets the auto brightness adjustment factor.
653      * <p>
654      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
655      * </p>
656      *
657      * @param adjustment The adjustment factor from -1.0 to 1.0.
658      *
659      * @hide Requires signature permission.
660      */
setTemporaryAutoBrightnessAdjustment(float adjustment)661     public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
662         try {
663             mDm.setTemporaryAutoBrightnessAdjustment(adjustment);
664         } catch (RemoteException ex) {
665             throw ex.rethrowFromSystemServer();
666         }
667     }
668 
669     /**
670      * Returns the minimum brightness curve, which guarantess that any brightness curve that dips
671      * below it is rejected by the system.
672      * This prevent auto-brightness from setting the screen so dark as to prevent the user from
673      * resetting or disabling it, and maps lux to the absolute minimum nits that are still readable
674      * in that ambient brightness.
675      *
676      * @return The minimum brightness curve (as lux values and their corresponding nits values).
677      */
getMinimumBrightnessCurve()678     public Pair<float[], float[]> getMinimumBrightnessCurve() {
679         try {
680             Curve curve = mDm.getMinimumBrightnessCurve();
681             return Pair.create(curve.getX(), curve.getY());
682         } catch (RemoteException ex) {
683             throw ex.rethrowFromSystemServer();
684         }
685     }
686 
687     /**
688      * Retrieves ambient brightness stats.
689      */
getAmbientBrightnessStats()690     public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
691         try {
692             ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
693             if (stats == null) {
694                 return Collections.emptyList();
695             }
696             return stats.getList();
697         } catch (RemoteException ex) {
698             throw ex.rethrowFromSystemServer();
699         }
700     }
701 
702     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
703         @Override
onDisplayEvent(int displayId, int event)704         public void onDisplayEvent(int displayId, int event) {
705             if (DEBUG) {
706                 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
707             }
708             handleDisplayEvent(displayId, event);
709         }
710     }
711 
712     private static final class DisplayListenerDelegate extends Handler {
713         public final DisplayListener mListener;
714 
DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper)715         DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper) {
716             super(looper, null, true /*async*/);
717             mListener = listener;
718         }
719 
sendDisplayEvent(int displayId, int event)720         public void sendDisplayEvent(int displayId, int event) {
721             Message msg = obtainMessage(event, displayId, 0);
722             sendMessage(msg);
723         }
724 
clearEvents()725         public void clearEvents() {
726             removeCallbacksAndMessages(null);
727         }
728 
729         @Override
handleMessage(Message msg)730         public void handleMessage(Message msg) {
731             switch (msg.what) {
732                 case EVENT_DISPLAY_ADDED:
733                     mListener.onDisplayAdded(msg.arg1);
734                     break;
735                 case EVENT_DISPLAY_CHANGED:
736                     mListener.onDisplayChanged(msg.arg1);
737                     break;
738                 case EVENT_DISPLAY_REMOVED:
739                     mListener.onDisplayRemoved(msg.arg1);
740                     break;
741             }
742         }
743     }
744 
745     private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
746         private VirtualDisplayCallbackDelegate mDelegate;
747 
VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler)748         public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) {
749             if (callback != null) {
750                 mDelegate = new VirtualDisplayCallbackDelegate(callback, handler);
751             }
752         }
753 
754         @Override // Binder call
onPaused()755         public void onPaused() {
756             if (mDelegate != null) {
757                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED);
758             }
759         }
760 
761         @Override // Binder call
onResumed()762         public void onResumed() {
763             if (mDelegate != null) {
764                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED);
765             }
766         }
767 
768         @Override // Binder call
onStopped()769         public void onStopped() {
770             if (mDelegate != null) {
771                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED);
772             }
773         }
774     }
775 
776     private final static class VirtualDisplayCallbackDelegate extends Handler {
777         public static final int MSG_DISPLAY_PAUSED = 0;
778         public static final int MSG_DISPLAY_RESUMED = 1;
779         public static final int MSG_DISPLAY_STOPPED = 2;
780 
781         private final VirtualDisplay.Callback mCallback;
782 
VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback, Handler handler)783         public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback,
784                 Handler handler) {
785             super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
786             mCallback = callback;
787         }
788 
789         @Override
handleMessage(Message msg)790         public void handleMessage(Message msg) {
791             switch (msg.what) {
792                 case MSG_DISPLAY_PAUSED:
793                     mCallback.onPaused();
794                     break;
795                 case MSG_DISPLAY_RESUMED:
796                     mCallback.onResumed();
797                     break;
798                 case MSG_DISPLAY_STOPPED:
799                     mCallback.onStopped();
800                     break;
801             }
802         }
803     }
804 
805     /**
806      * Name of the property containing a unique token which changes every time we update the
807      * system's display configuration.
808      */
809     public static final String CACHE_KEY_DISPLAY_INFO_PROPERTY =
810             "cache_key.display_info";
811 
812     /**
813      * Invalidates the contents of the display info cache for all applications. Can only
814      * be called by system_server.
815      */
invalidateLocalDisplayInfoCaches()816     public static void invalidateLocalDisplayInfoCaches() {
817         PropertyInvalidatedCache.invalidateCache(CACHE_KEY_DISPLAY_INFO_PROPERTY);
818     }
819 
820     /**
821      * Disables the binder call cache.
822      */
disableLocalDisplayInfoCaches()823     public void disableLocalDisplayInfoCaches() {
824         mDisplayCache = null;
825     }
826 
nSignalNativeCallbacks(float refreshRate)827     private static native void nSignalNativeCallbacks(float refreshRate);
828 
829     // Called from AChoreographer via JNI.
830     // Registers AChoreographer so that refresh rate callbacks can be dispatched from DMS.
registerNativeChoreographerForRefreshRateCallbacks()831     private void registerNativeChoreographerForRefreshRateCallbacks() {
832         synchronized (mLock) {
833             registerCallbackIfNeededLocked();
834             mDispatchNativeCallbacks = true;
835             DisplayInfo display = getDisplayInfoLocked(Display.DEFAULT_DISPLAY);
836             if (display != null) {
837                 // We need to tell AChoreographer instances the current refresh rate so that apps
838                 // can get it for free once a callback first registers.
839                 float refreshRate = display.getMode().getRefreshRate();
840                 nSignalNativeCallbacks(refreshRate);
841             }
842         }
843     }
844 
845     // Called from AChoreographer via JNI.
846     // Unregisters AChoreographer from receiving refresh rate callbacks.
unregisterNativeChoreographerForRefreshRateCallbacks()847     private void unregisterNativeChoreographerForRefreshRateCallbacks() {
848         synchronized (mLock) {
849             mDispatchNativeCallbacks = false;
850         }
851     }
852 }
853