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.content.Context;
20 import android.content.pm.ParceledListSlice;
21 import android.content.res.Resources;
22 import android.graphics.Point;
23 import android.hardware.display.DisplayManager.DisplayListener;
24 import android.media.projection.IMediaProjection;
25 import android.media.projection.MediaProjection;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.os.RemoteException;
31 import android.os.ServiceManager;
32 import android.text.TextUtils;
33 import android.util.Log;
34 import android.util.Pair;
35 import android.util.SparseArray;
36 import android.view.Display;
37 import android.view.DisplayAdjustments;
38 import android.view.DisplayInfo;
39 import android.view.Surface;
40 
41 import java.util.ArrayList;
42 import java.util.Collections;
43 import java.util.List;
44 
45 /**
46  * Manager communication with the display manager service on behalf of
47  * an application process.  You're probably looking for {@link DisplayManager}.
48  *
49  * @hide
50  */
51 public final class DisplayManagerGlobal {
52     private static final String TAG = "DisplayManager";
53     private static final boolean DEBUG = false;
54 
55     // True if display info and display ids should be cached.
56     //
57     // FIXME: The cache is currently disabled because it's unclear whether we have the
58     // necessary guarantees that the caches will always be flushed before clients
59     // attempt to observe their new state.  For example, depending on the order
60     // in which the binder transactions take place, we might have a problem where
61     // an application could start processing a configuration change due to a display
62     // orientation change before the display info cache has actually been invalidated.
63     private static final boolean USE_CACHE = false;
64 
65     public static final int EVENT_DISPLAY_ADDED = 1;
66     public static final int EVENT_DISPLAY_CHANGED = 2;
67     public static final int EVENT_DISPLAY_REMOVED = 3;
68 
69     private static DisplayManagerGlobal sInstance;
70 
71     private final Object mLock = new Object();
72 
73     private final IDisplayManager mDm;
74 
75     private DisplayManagerCallback mCallback;
76     private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
77             new ArrayList<DisplayListenerDelegate>();
78 
79     private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>();
80     private int[] mDisplayIdCache;
81 
82     private int mWifiDisplayScanNestCount;
83 
DisplayManagerGlobal(IDisplayManager dm)84     private DisplayManagerGlobal(IDisplayManager dm) {
85         mDm = dm;
86     }
87 
88     /**
89      * Gets an instance of the display manager global singleton.
90      *
91      * @return The display manager instance, may be null early in system startup
92      * before the display manager has been fully initialized.
93      */
getInstance()94     public static DisplayManagerGlobal getInstance() {
95         synchronized (DisplayManagerGlobal.class) {
96             if (sInstance == null) {
97                 IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
98                 if (b != null) {
99                     sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
100                 }
101             }
102             return sInstance;
103         }
104     }
105 
106     /**
107      * Get information about a particular logical display.
108      *
109      * @param displayId The logical display id.
110      * @return Information about the specified display, or null if it does not exist.
111      * This object belongs to an internal cache and should be treated as if it were immutable.
112      */
getDisplayInfo(int displayId)113     public DisplayInfo getDisplayInfo(int displayId) {
114         try {
115             synchronized (mLock) {
116                 DisplayInfo info;
117                 if (USE_CACHE) {
118                     info = mDisplayInfoCache.get(displayId);
119                     if (info != null) {
120                         return info;
121                     }
122                 }
123 
124                 info = mDm.getDisplayInfo(displayId);
125                 if (info == null) {
126                     return null;
127                 }
128 
129                 if (USE_CACHE) {
130                     mDisplayInfoCache.put(displayId, info);
131                 }
132                 registerCallbackIfNeededLocked();
133 
134                 if (DEBUG) {
135                     Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
136                 }
137                 return info;
138             }
139         } catch (RemoteException ex) {
140             throw ex.rethrowFromSystemServer();
141         }
142     }
143 
144     /**
145      * Gets all currently valid logical display ids.
146      *
147      * @return An array containing all display ids.
148      */
getDisplayIds()149     public int[] getDisplayIds() {
150         try {
151             synchronized (mLock) {
152                 if (USE_CACHE) {
153                     if (mDisplayIdCache != null) {
154                         return mDisplayIdCache;
155                     }
156                 }
157 
158                 int[] displayIds = mDm.getDisplayIds();
159                 if (USE_CACHE) {
160                     mDisplayIdCache = displayIds;
161                 }
162                 registerCallbackIfNeededLocked();
163                 return displayIds;
164             }
165         } catch (RemoteException ex) {
166             throw ex.rethrowFromSystemServer();
167         }
168     }
169 
170     /**
171      * Gets information about a logical display.
172      *
173      * The display metrics may be adjusted to provide compatibility
174      * for legacy applications or limited screen areas.
175      *
176      * @param displayId The logical display id.
177      * @param daj The compatibility info and activityToken.
178      * @return The display object, or null if there is no display with the given id.
179      */
getCompatibleDisplay(int displayId, DisplayAdjustments daj)180     public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
181         DisplayInfo displayInfo = getDisplayInfo(displayId);
182         if (displayInfo == null) {
183             return null;
184         }
185         return new Display(this, displayId, displayInfo, daj);
186     }
187 
188     /**
189      * Gets information about a logical display.
190      *
191      * The display metrics may be adjusted to provide compatibility
192      * for legacy applications or limited screen areas.
193      *
194      * @param displayId The logical display id.
195      * @param resources Resources providing compatibility info.
196      * @return The display object, or null if there is no display with the given id.
197      */
getCompatibleDisplay(int displayId, Resources resources)198     public Display getCompatibleDisplay(int displayId, Resources resources) {
199         DisplayInfo displayInfo = getDisplayInfo(displayId);
200         if (displayInfo == null) {
201             return null;
202         }
203         return new Display(this, displayId, displayInfo, resources);
204     }
205 
206     /**
207      * Gets information about a logical display without applying any compatibility metrics.
208      *
209      * @param displayId The logical display id.
210      * @return The display object, or null if there is no display with the given id.
211      */
getRealDisplay(int displayId)212     public Display getRealDisplay(int displayId) {
213         return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
214     }
215 
registerDisplayListener(DisplayListener listener, Handler handler)216     public void registerDisplayListener(DisplayListener listener, Handler handler) {
217         if (listener == null) {
218             throw new IllegalArgumentException("listener must not be null");
219         }
220 
221         synchronized (mLock) {
222             int index = findDisplayListenerLocked(listener);
223             if (index < 0) {
224                 mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
225                 registerCallbackIfNeededLocked();
226             }
227         }
228     }
229 
unregisterDisplayListener(DisplayListener listener)230     public void unregisterDisplayListener(DisplayListener listener) {
231         if (listener == null) {
232             throw new IllegalArgumentException("listener must not be null");
233         }
234 
235         synchronized (mLock) {
236             int index = findDisplayListenerLocked(listener);
237             if (index >= 0) {
238                 DisplayListenerDelegate d = mDisplayListeners.get(index);
239                 d.clearEvents();
240                 mDisplayListeners.remove(index);
241             }
242         }
243     }
244 
findDisplayListenerLocked(DisplayListener listener)245     private int findDisplayListenerLocked(DisplayListener listener) {
246         final int numListeners = mDisplayListeners.size();
247         for (int i = 0; i < numListeners; i++) {
248             if (mDisplayListeners.get(i).mListener == listener) {
249                 return i;
250             }
251         }
252         return -1;
253     }
254 
registerCallbackIfNeededLocked()255     private void registerCallbackIfNeededLocked() {
256         if (mCallback == null) {
257             mCallback = new DisplayManagerCallback();
258             try {
259                 mDm.registerCallback(mCallback);
260             } catch (RemoteException ex) {
261                 throw ex.rethrowFromSystemServer();
262             }
263         }
264     }
265 
handleDisplayEvent(int displayId, int event)266     private void handleDisplayEvent(int displayId, int event) {
267         synchronized (mLock) {
268             if (USE_CACHE) {
269                 mDisplayInfoCache.remove(displayId);
270 
271                 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
272                     mDisplayIdCache = null;
273                 }
274             }
275 
276             final int numListeners = mDisplayListeners.size();
277             for (int i = 0; i < numListeners; i++) {
278                 mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
279             }
280         }
281     }
282 
startWifiDisplayScan()283     public void startWifiDisplayScan() {
284         synchronized (mLock) {
285             if (mWifiDisplayScanNestCount++ == 0) {
286                 registerCallbackIfNeededLocked();
287                 try {
288                     mDm.startWifiDisplayScan();
289                 } catch (RemoteException ex) {
290                     throw ex.rethrowFromSystemServer();
291                 }
292             }
293         }
294     }
295 
stopWifiDisplayScan()296     public void stopWifiDisplayScan() {
297         synchronized (mLock) {
298             if (--mWifiDisplayScanNestCount == 0) {
299                 try {
300                     mDm.stopWifiDisplayScan();
301                 } catch (RemoteException ex) {
302                     throw ex.rethrowFromSystemServer();
303                 }
304             } else if (mWifiDisplayScanNestCount < 0) {
305                 Log.wtf(TAG, "Wifi display scan nest count became negative: "
306                         + mWifiDisplayScanNestCount);
307                 mWifiDisplayScanNestCount = 0;
308             }
309         }
310     }
311 
connectWifiDisplay(String deviceAddress)312     public void connectWifiDisplay(String deviceAddress) {
313         if (deviceAddress == null) {
314             throw new IllegalArgumentException("deviceAddress must not be null");
315         }
316 
317         try {
318             mDm.connectWifiDisplay(deviceAddress);
319         } catch (RemoteException ex) {
320             throw ex.rethrowFromSystemServer();
321         }
322     }
323 
pauseWifiDisplay()324     public void pauseWifiDisplay() {
325         try {
326             mDm.pauseWifiDisplay();
327         } catch (RemoteException ex) {
328             throw ex.rethrowFromSystemServer();
329         }
330     }
331 
resumeWifiDisplay()332     public void resumeWifiDisplay() {
333         try {
334             mDm.resumeWifiDisplay();
335         } catch (RemoteException ex) {
336             throw ex.rethrowFromSystemServer();
337         }
338     }
339 
disconnectWifiDisplay()340     public void disconnectWifiDisplay() {
341         try {
342             mDm.disconnectWifiDisplay();
343         } catch (RemoteException ex) {
344             throw ex.rethrowFromSystemServer();
345         }
346     }
347 
renameWifiDisplay(String deviceAddress, String alias)348     public void renameWifiDisplay(String deviceAddress, String alias) {
349         if (deviceAddress == null) {
350             throw new IllegalArgumentException("deviceAddress must not be null");
351         }
352 
353         try {
354             mDm.renameWifiDisplay(deviceAddress, alias);
355         } catch (RemoteException ex) {
356             throw ex.rethrowFromSystemServer();
357         }
358     }
359 
forgetWifiDisplay(String deviceAddress)360     public void forgetWifiDisplay(String deviceAddress) {
361         if (deviceAddress == null) {
362             throw new IllegalArgumentException("deviceAddress must not be null");
363         }
364 
365         try {
366             mDm.forgetWifiDisplay(deviceAddress);
367         } catch (RemoteException ex) {
368             throw ex.rethrowFromSystemServer();
369         }
370     }
371 
getWifiDisplayStatus()372     public WifiDisplayStatus getWifiDisplayStatus() {
373         try {
374             return mDm.getWifiDisplayStatus();
375         } catch (RemoteException ex) {
376             throw ex.rethrowFromSystemServer();
377         }
378     }
379 
requestColorMode(int displayId, int colorMode)380     public void requestColorMode(int displayId, int colorMode) {
381         try {
382             mDm.requestColorMode(displayId, colorMode);
383         } catch (RemoteException ex) {
384             throw ex.rethrowFromSystemServer();
385         }
386     }
387 
388     /**
389      * Set the level of color saturation to apply to the display.
390      */
setSaturationLevel(float level)391     public void setSaturationLevel(float level) {
392         try {
393             mDm.setSaturationLevel(level);
394         } catch (RemoteException ex) {
395             throw ex.rethrowFromSystemServer();
396         }
397     }
398 
createVirtualDisplay(Context context, MediaProjection projection, String name, int width, int height, int densityDpi, Surface surface, int flags, VirtualDisplay.Callback callback, Handler handler, String uniqueId)399     public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
400             String name, int width, int height, int densityDpi, Surface surface, int flags,
401             VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
402         if (TextUtils.isEmpty(name)) {
403             throw new IllegalArgumentException("name must be non-null and non-empty");
404         }
405         if (width <= 0 || height <= 0 || densityDpi <= 0) {
406             throw new IllegalArgumentException("width, height, and densityDpi must be "
407                     + "greater than 0");
408         }
409 
410         VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
411         IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
412         int displayId;
413         try {
414             displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken,
415                     context.getPackageName(), name, width, height, densityDpi, surface, flags,
416                     uniqueId);
417         } catch (RemoteException ex) {
418             throw ex.rethrowFromSystemServer();
419         }
420         if (displayId < 0) {
421             Log.e(TAG, "Could not create virtual display: " + name);
422             return null;
423         }
424         Display display = getRealDisplay(displayId);
425         if (display == null) {
426             Log.wtf(TAG, "Could not obtain display info for newly created "
427                     + "virtual display: " + name);
428             try {
429                 mDm.releaseVirtualDisplay(callbackWrapper);
430             } catch (RemoteException ex) {
431                 throw ex.rethrowFromSystemServer();
432             }
433             return null;
434         }
435         return new VirtualDisplay(this, display, callbackWrapper, surface);
436     }
437 
setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface)438     public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
439         try {
440             mDm.setVirtualDisplaySurface(token, surface);
441         } catch (RemoteException ex) {
442             throw ex.rethrowFromSystemServer();
443         }
444     }
445 
resizeVirtualDisplay(IVirtualDisplayCallback token, int width, int height, int densityDpi)446     public void resizeVirtualDisplay(IVirtualDisplayCallback token,
447             int width, int height, int densityDpi) {
448         try {
449             mDm.resizeVirtualDisplay(token, width, height, densityDpi);
450         } catch (RemoteException ex) {
451             throw ex.rethrowFromSystemServer();
452         }
453     }
454 
releaseVirtualDisplay(IVirtualDisplayCallback token)455     public void releaseVirtualDisplay(IVirtualDisplayCallback token) {
456         try {
457             mDm.releaseVirtualDisplay(token);
458         } catch (RemoteException ex) {
459             throw ex.rethrowFromSystemServer();
460         }
461     }
462 
463     /**
464      * Gets the stable device display size, in pixels.
465      */
getStableDisplaySize()466     public Point getStableDisplaySize() {
467         try {
468             return mDm.getStableDisplaySize();
469         } catch (RemoteException ex) {
470             throw ex.rethrowFromSystemServer();
471         }
472     }
473 
474     /**
475      * Retrieves brightness change events.
476      */
getBrightnessEvents(String callingPackage)477     public List<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
478         try {
479             ParceledListSlice<BrightnessChangeEvent> events =
480                     mDm.getBrightnessEvents(callingPackage);
481             if (events == null) {
482                 return Collections.emptyList();
483             }
484             return events.getList();
485         } catch (RemoteException ex) {
486             throw ex.rethrowFromSystemServer();
487         }
488     }
489 
490     /**
491      * Sets the global brightness configuration for a given user.
492      *
493      * @hide
494      */
setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId, String packageName)495     public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId,
496             String packageName) {
497         try {
498             mDm.setBrightnessConfigurationForUser(c, userId, packageName);
499         } catch (RemoteException ex) {
500             throw ex.rethrowFromSystemServer();
501         }
502     }
503 
504     /**
505      * Gets the global brightness configuration for a given user or null if one hasn't been set.
506      *
507      * @hide
508      */
getBrightnessConfigurationForUser(int userId)509     public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
510         try {
511             return mDm.getBrightnessConfigurationForUser(userId);
512         } catch (RemoteException ex) {
513             throw ex.rethrowFromSystemServer();
514         }
515     }
516 
517     /**
518      * Gets the default brightness configuration or null if one hasn't been configured.
519      *
520      * @hide
521      */
getDefaultBrightnessConfiguration()522     public BrightnessConfiguration getDefaultBrightnessConfiguration() {
523         try {
524             return mDm.getDefaultBrightnessConfiguration();
525         } catch (RemoteException ex) {
526             throw ex.rethrowFromSystemServer();
527         }
528     }
529 
530     /**
531      * Temporarily sets the brightness of the display.
532      * <p>
533      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
534      * </p>
535      *
536      * @param brightness The brightness value from 0 to 255.
537      *
538      * @hide Requires signature permission.
539      */
setTemporaryBrightness(int brightness)540     public void setTemporaryBrightness(int brightness) {
541         try {
542             mDm.setTemporaryBrightness(brightness);
543         } catch (RemoteException ex) {
544             throw ex.rethrowFromSystemServer();
545         }
546     }
547 
548     /**
549      * Temporarily sets the auto brightness adjustment factor.
550      * <p>
551      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
552      * </p>
553      *
554      * @param adjustment The adjustment factor from -1.0 to 1.0.
555      *
556      * @hide Requires signature permission.
557      */
setTemporaryAutoBrightnessAdjustment(float adjustment)558     public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
559         try {
560             mDm.setTemporaryAutoBrightnessAdjustment(adjustment);
561         } catch (RemoteException ex) {
562             throw ex.rethrowFromSystemServer();
563         }
564     }
565 
566     /**
567      * Returns the minimum brightness curve, which guarantess that any brightness curve that dips
568      * below it is rejected by the system.
569      * This prevent auto-brightness from setting the screen so dark as to prevent the user from
570      * resetting or disabling it, and maps lux to the absolute minimum nits that are still readable
571      * in that ambient brightness.
572      *
573      * @return The minimum brightness curve (as lux values and their corresponding nits values).
574      */
getMinimumBrightnessCurve()575     public Pair<float[], float[]> getMinimumBrightnessCurve() {
576         try {
577             Curve curve = mDm.getMinimumBrightnessCurve();
578             return Pair.create(curve.getX(), curve.getY());
579         } catch (RemoteException ex) {
580             throw ex.rethrowFromSystemServer();
581         }
582     }
583 
584     /**
585      * Retrieves ambient brightness stats.
586      */
getAmbientBrightnessStats()587     public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
588         try {
589             ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
590             if (stats == null) {
591                 return Collections.emptyList();
592             }
593             return stats.getList();
594         } catch (RemoteException ex) {
595             throw ex.rethrowFromSystemServer();
596         }
597     }
598 
599     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
600         @Override
onDisplayEvent(int displayId, int event)601         public void onDisplayEvent(int displayId, int event) {
602             if (DEBUG) {
603                 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
604             }
605             handleDisplayEvent(displayId, event);
606         }
607     }
608 
609     private static final class DisplayListenerDelegate extends Handler {
610         public final DisplayListener mListener;
611 
DisplayListenerDelegate(DisplayListener listener, Handler handler)612         public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
613             super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
614             mListener = listener;
615         }
616 
sendDisplayEvent(int displayId, int event)617         public void sendDisplayEvent(int displayId, int event) {
618             Message msg = obtainMessage(event, displayId, 0);
619             sendMessage(msg);
620         }
621 
clearEvents()622         public void clearEvents() {
623             removeCallbacksAndMessages(null);
624         }
625 
626         @Override
handleMessage(Message msg)627         public void handleMessage(Message msg) {
628             switch (msg.what) {
629                 case EVENT_DISPLAY_ADDED:
630                     mListener.onDisplayAdded(msg.arg1);
631                     break;
632                 case EVENT_DISPLAY_CHANGED:
633                     mListener.onDisplayChanged(msg.arg1);
634                     break;
635                 case EVENT_DISPLAY_REMOVED:
636                     mListener.onDisplayRemoved(msg.arg1);
637                     break;
638             }
639         }
640     }
641 
642     private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
643         private VirtualDisplayCallbackDelegate mDelegate;
644 
VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler)645         public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) {
646             if (callback != null) {
647                 mDelegate = new VirtualDisplayCallbackDelegate(callback, handler);
648             }
649         }
650 
651         @Override // Binder call
onPaused()652         public void onPaused() {
653             if (mDelegate != null) {
654                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED);
655             }
656         }
657 
658         @Override // Binder call
onResumed()659         public void onResumed() {
660             if (mDelegate != null) {
661                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED);
662             }
663         }
664 
665         @Override // Binder call
onStopped()666         public void onStopped() {
667             if (mDelegate != null) {
668                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED);
669             }
670         }
671     }
672 
673     private final static class VirtualDisplayCallbackDelegate extends Handler {
674         public static final int MSG_DISPLAY_PAUSED = 0;
675         public static final int MSG_DISPLAY_RESUMED = 1;
676         public static final int MSG_DISPLAY_STOPPED = 2;
677 
678         private final VirtualDisplay.Callback mCallback;
679 
VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback, Handler handler)680         public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback,
681                 Handler handler) {
682             super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
683             mCallback = callback;
684         }
685 
686         @Override
handleMessage(Message msg)687         public void handleMessage(Message msg) {
688             switch (msg.what) {
689                 case MSG_DISPLAY_PAUSED:
690                     mCallback.onPaused();
691                     break;
692                 case MSG_DISPLAY_RESUMED:
693                     mCallback.onResumed();
694                     break;
695                 case MSG_DISPLAY_STOPPED:
696                     mCallback.onStopped();
697                     break;
698             }
699         }
700     }
701 }
702