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.res.Configuration;
21 import android.content.res.Resources;
22 import android.hardware.display.DisplayManager.DisplayListener;
23 import android.media.projection.MediaProjection;
24 import android.media.projection.IMediaProjection;
25 import android.os.Handler;
26 import android.os.IBinder;
27 import android.os.Looper;
28 import android.os.Message;
29 import android.os.RemoteException;
30 import android.os.ServiceManager;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.util.SparseArray;
34 import android.view.DisplayAdjustments;
35 import android.view.Display;
36 import android.view.DisplayInfo;
37 import android.view.Surface;
38 
39 import java.util.ArrayList;
40 
41 /**
42  * Manager communication with the display manager service on behalf of
43  * an application process.  You're probably looking for {@link DisplayManager}.
44  *
45  * @hide
46  */
47 public final class DisplayManagerGlobal {
48     private static final String TAG = "DisplayManager";
49     private static final boolean DEBUG = false;
50 
51     // True if display info and display ids should be cached.
52     //
53     // FIXME: The cache is currently disabled because it's unclear whether we have the
54     // necessary guarantees that the caches will always be flushed before clients
55     // attempt to observe their new state.  For example, depending on the order
56     // in which the binder transactions take place, we might have a problem where
57     // an application could start processing a configuration change due to a display
58     // orientation change before the display info cache has actually been invalidated.
59     private static final boolean USE_CACHE = false;
60 
61     public static final int EVENT_DISPLAY_ADDED = 1;
62     public static final int EVENT_DISPLAY_CHANGED = 2;
63     public static final int EVENT_DISPLAY_REMOVED = 3;
64 
65     private static DisplayManagerGlobal sInstance;
66 
67     private final Object mLock = new Object();
68 
69     private final IDisplayManager mDm;
70 
71     private DisplayManagerCallback mCallback;
72     private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
73             new ArrayList<DisplayListenerDelegate>();
74 
75     private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>();
76     private int[] mDisplayIdCache;
77 
78     private int mWifiDisplayScanNestCount;
79 
DisplayManagerGlobal(IDisplayManager dm)80     private DisplayManagerGlobal(IDisplayManager dm) {
81         mDm = dm;
82     }
83 
84     /**
85      * Gets an instance of the display manager global singleton.
86      *
87      * @return The display manager instance, may be null early in system startup
88      * before the display manager has been fully initialized.
89      */
getInstance()90     public static DisplayManagerGlobal getInstance() {
91         synchronized (DisplayManagerGlobal.class) {
92             if (sInstance == null) {
93                 IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
94                 if (b != null) {
95                     sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
96                 }
97             }
98             return sInstance;
99         }
100     }
101 
102     /**
103      * Get information about a particular logical display.
104      *
105      * @param displayId The logical display id.
106      * @return Information about the specified display, or null if it does not exist.
107      * This object belongs to an internal cache and should be treated as if it were immutable.
108      */
getDisplayInfo(int displayId)109     public DisplayInfo getDisplayInfo(int displayId) {
110         try {
111             synchronized (mLock) {
112                 DisplayInfo info;
113                 if (USE_CACHE) {
114                     info = mDisplayInfoCache.get(displayId);
115                     if (info != null) {
116                         return info;
117                     }
118                 }
119 
120                 info = mDm.getDisplayInfo(displayId);
121                 if (info == null) {
122                     return null;
123                 }
124 
125                 if (USE_CACHE) {
126                     mDisplayInfoCache.put(displayId, info);
127                 }
128                 registerCallbackIfNeededLocked();
129 
130                 if (DEBUG) {
131                     Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
132                 }
133                 return info;
134             }
135         } catch (RemoteException ex) {
136             throw ex.rethrowFromSystemServer();
137         }
138     }
139 
140     /**
141      * Gets all currently valid logical display ids.
142      *
143      * @return An array containing all display ids.
144      */
getDisplayIds()145     public int[] getDisplayIds() {
146         try {
147             synchronized (mLock) {
148                 if (USE_CACHE) {
149                     if (mDisplayIdCache != null) {
150                         return mDisplayIdCache;
151                     }
152                 }
153 
154                 int[] displayIds = mDm.getDisplayIds();
155                 if (USE_CACHE) {
156                     mDisplayIdCache = displayIds;
157                 }
158                 registerCallbackIfNeededLocked();
159                 return displayIds;
160             }
161         } catch (RemoteException ex) {
162             throw ex.rethrowFromSystemServer();
163         }
164     }
165 
166     /**
167      * Gets information about a logical display.
168      *
169      * The display metrics may be adjusted to provide compatibility
170      * for legacy applications or limited screen areas.
171      *
172      * @param displayId The logical display id.
173      * @param daj The compatibility info and activityToken.
174      * @return The display object, or null if there is no display with the given id.
175      */
getCompatibleDisplay(int displayId, DisplayAdjustments daj)176     public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
177         DisplayInfo displayInfo = getDisplayInfo(displayId);
178         if (displayInfo == null) {
179             return null;
180         }
181         return new Display(this, displayId, displayInfo, daj);
182     }
183 
184     /**
185      * Gets information about a logical display.
186      *
187      * The display metrics may be adjusted to provide compatibility
188      * for legacy applications or limited screen areas.
189      *
190      * @param displayId The logical display id.
191      * @param resources Resources providing compatibility info.
192      * @return The display object, or null if there is no display with the given id.
193      */
getCompatibleDisplay(int displayId, Resources resources)194     public Display getCompatibleDisplay(int displayId, Resources resources) {
195         DisplayInfo displayInfo = getDisplayInfo(displayId);
196         if (displayInfo == null) {
197             return null;
198         }
199         return new Display(this, displayId, displayInfo, resources);
200     }
201 
202     /**
203      * Gets information about a logical display without applying any compatibility metrics.
204      *
205      * @param displayId The logical display id.
206      * @return The display object, or null if there is no display with the given id.
207      */
getRealDisplay(int displayId)208     public Display getRealDisplay(int displayId) {
209         return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
210     }
211 
registerDisplayListener(DisplayListener listener, Handler handler)212     public void registerDisplayListener(DisplayListener listener, Handler handler) {
213         if (listener == null) {
214             throw new IllegalArgumentException("listener must not be null");
215         }
216 
217         synchronized (mLock) {
218             int index = findDisplayListenerLocked(listener);
219             if (index < 0) {
220                 mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
221                 registerCallbackIfNeededLocked();
222             }
223         }
224     }
225 
unregisterDisplayListener(DisplayListener listener)226     public void unregisterDisplayListener(DisplayListener listener) {
227         if (listener == null) {
228             throw new IllegalArgumentException("listener must not be null");
229         }
230 
231         synchronized (mLock) {
232             int index = findDisplayListenerLocked(listener);
233             if (index >= 0) {
234                 DisplayListenerDelegate d = mDisplayListeners.get(index);
235                 d.clearEvents();
236                 mDisplayListeners.remove(index);
237             }
238         }
239     }
240 
findDisplayListenerLocked(DisplayListener listener)241     private int findDisplayListenerLocked(DisplayListener listener) {
242         final int numListeners = mDisplayListeners.size();
243         for (int i = 0; i < numListeners; i++) {
244             if (mDisplayListeners.get(i).mListener == listener) {
245                 return i;
246             }
247         }
248         return -1;
249     }
250 
registerCallbackIfNeededLocked()251     private void registerCallbackIfNeededLocked() {
252         if (mCallback == null) {
253             mCallback = new DisplayManagerCallback();
254             try {
255                 mDm.registerCallback(mCallback);
256             } catch (RemoteException ex) {
257                 throw ex.rethrowFromSystemServer();
258             }
259         }
260     }
261 
handleDisplayEvent(int displayId, int event)262     private void handleDisplayEvent(int displayId, int event) {
263         synchronized (mLock) {
264             if (USE_CACHE) {
265                 mDisplayInfoCache.remove(displayId);
266 
267                 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
268                     mDisplayIdCache = null;
269                 }
270             }
271 
272             final int numListeners = mDisplayListeners.size();
273             for (int i = 0; i < numListeners; i++) {
274                 mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
275             }
276         }
277     }
278 
startWifiDisplayScan()279     public void startWifiDisplayScan() {
280         synchronized (mLock) {
281             if (mWifiDisplayScanNestCount++ == 0) {
282                 registerCallbackIfNeededLocked();
283                 try {
284                     mDm.startWifiDisplayScan();
285                 } catch (RemoteException ex) {
286                     throw ex.rethrowFromSystemServer();
287                 }
288             }
289         }
290     }
291 
stopWifiDisplayScan()292     public void stopWifiDisplayScan() {
293         synchronized (mLock) {
294             if (--mWifiDisplayScanNestCount == 0) {
295                 try {
296                     mDm.stopWifiDisplayScan();
297                 } catch (RemoteException ex) {
298                     throw ex.rethrowFromSystemServer();
299                 }
300             } else if (mWifiDisplayScanNestCount < 0) {
301                 Log.wtf(TAG, "Wifi display scan nest count became negative: "
302                         + mWifiDisplayScanNestCount);
303                 mWifiDisplayScanNestCount = 0;
304             }
305         }
306     }
307 
connectWifiDisplay(String deviceAddress)308     public void connectWifiDisplay(String deviceAddress) {
309         if (deviceAddress == null) {
310             throw new IllegalArgumentException("deviceAddress must not be null");
311         }
312 
313         try {
314             mDm.connectWifiDisplay(deviceAddress);
315         } catch (RemoteException ex) {
316             throw ex.rethrowFromSystemServer();
317         }
318     }
319 
pauseWifiDisplay()320     public void pauseWifiDisplay() {
321         try {
322             mDm.pauseWifiDisplay();
323         } catch (RemoteException ex) {
324             throw ex.rethrowFromSystemServer();
325         }
326     }
327 
resumeWifiDisplay()328     public void resumeWifiDisplay() {
329         try {
330             mDm.resumeWifiDisplay();
331         } catch (RemoteException ex) {
332             throw ex.rethrowFromSystemServer();
333         }
334     }
335 
disconnectWifiDisplay()336     public void disconnectWifiDisplay() {
337         try {
338             mDm.disconnectWifiDisplay();
339         } catch (RemoteException ex) {
340             throw ex.rethrowFromSystemServer();
341         }
342     }
343 
renameWifiDisplay(String deviceAddress, String alias)344     public void renameWifiDisplay(String deviceAddress, String alias) {
345         if (deviceAddress == null) {
346             throw new IllegalArgumentException("deviceAddress must not be null");
347         }
348 
349         try {
350             mDm.renameWifiDisplay(deviceAddress, alias);
351         } catch (RemoteException ex) {
352             throw ex.rethrowFromSystemServer();
353         }
354     }
355 
forgetWifiDisplay(String deviceAddress)356     public void forgetWifiDisplay(String deviceAddress) {
357         if (deviceAddress == null) {
358             throw new IllegalArgumentException("deviceAddress must not be null");
359         }
360 
361         try {
362             mDm.forgetWifiDisplay(deviceAddress);
363         } catch (RemoteException ex) {
364             throw ex.rethrowFromSystemServer();
365         }
366     }
367 
getWifiDisplayStatus()368     public WifiDisplayStatus getWifiDisplayStatus() {
369         try {
370             return mDm.getWifiDisplayStatus();
371         } catch (RemoteException ex) {
372             throw ex.rethrowFromSystemServer();
373         }
374     }
375 
requestColorMode(int displayId, int colorMode)376     public void requestColorMode(int displayId, int colorMode) {
377         try {
378             mDm.requestColorMode(displayId, colorMode);
379         } catch (RemoteException ex) {
380             throw ex.rethrowFromSystemServer();
381         }
382     }
383 
createVirtualDisplay(Context context, MediaProjection projection, String name, int width, int height, int densityDpi, Surface surface, int flags, VirtualDisplay.Callback callback, Handler handler, String uniqueId)384     public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
385             String name, int width, int height, int densityDpi, Surface surface, int flags,
386             VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
387         if (TextUtils.isEmpty(name)) {
388             throw new IllegalArgumentException("name must be non-null and non-empty");
389         }
390         if (width <= 0 || height <= 0 || densityDpi <= 0) {
391             throw new IllegalArgumentException("width, height, and densityDpi must be "
392                     + "greater than 0");
393         }
394 
395         VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
396         IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
397         int displayId;
398         try {
399             displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken,
400                     context.getPackageName(), name, width, height, densityDpi, surface, flags,
401                     uniqueId);
402         } catch (RemoteException ex) {
403             throw ex.rethrowFromSystemServer();
404         }
405         if (displayId < 0) {
406             Log.e(TAG, "Could not create virtual display: " + name);
407             return null;
408         }
409         Display display = getRealDisplay(displayId);
410         if (display == null) {
411             Log.wtf(TAG, "Could not obtain display info for newly created "
412                     + "virtual display: " + name);
413             try {
414                 mDm.releaseVirtualDisplay(callbackWrapper);
415             } catch (RemoteException ex) {
416                 throw ex.rethrowFromSystemServer();
417             }
418             return null;
419         }
420         return new VirtualDisplay(this, display, callbackWrapper, surface);
421     }
422 
setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface)423     public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
424         try {
425             mDm.setVirtualDisplaySurface(token, surface);
426         } catch (RemoteException ex) {
427             throw ex.rethrowFromSystemServer();
428         }
429     }
430 
resizeVirtualDisplay(IVirtualDisplayCallback token, int width, int height, int densityDpi)431     public void resizeVirtualDisplay(IVirtualDisplayCallback token,
432             int width, int height, int densityDpi) {
433         try {
434             mDm.resizeVirtualDisplay(token, width, height, densityDpi);
435         } catch (RemoteException ex) {
436             throw ex.rethrowFromSystemServer();
437         }
438     }
439 
releaseVirtualDisplay(IVirtualDisplayCallback token)440     public void releaseVirtualDisplay(IVirtualDisplayCallback token) {
441         try {
442             mDm.releaseVirtualDisplay(token);
443         } catch (RemoteException ex) {
444             throw ex.rethrowFromSystemServer();
445         }
446     }
447 
448     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
449         @Override
onDisplayEvent(int displayId, int event)450         public void onDisplayEvent(int displayId, int event) {
451             if (DEBUG) {
452                 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
453             }
454             handleDisplayEvent(displayId, event);
455         }
456     }
457 
458     private static final class DisplayListenerDelegate extends Handler {
459         public final DisplayListener mListener;
460 
DisplayListenerDelegate(DisplayListener listener, Handler handler)461         public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
462             super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
463             mListener = listener;
464         }
465 
sendDisplayEvent(int displayId, int event)466         public void sendDisplayEvent(int displayId, int event) {
467             Message msg = obtainMessage(event, displayId, 0);
468             sendMessage(msg);
469         }
470 
clearEvents()471         public void clearEvents() {
472             removeCallbacksAndMessages(null);
473         }
474 
475         @Override
handleMessage(Message msg)476         public void handleMessage(Message msg) {
477             switch (msg.what) {
478                 case EVENT_DISPLAY_ADDED:
479                     mListener.onDisplayAdded(msg.arg1);
480                     break;
481                 case EVENT_DISPLAY_CHANGED:
482                     mListener.onDisplayChanged(msg.arg1);
483                     break;
484                 case EVENT_DISPLAY_REMOVED:
485                     mListener.onDisplayRemoved(msg.arg1);
486                     break;
487             }
488         }
489     }
490 
491     private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
492         private VirtualDisplayCallbackDelegate mDelegate;
493 
VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler)494         public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) {
495             if (callback != null) {
496                 mDelegate = new VirtualDisplayCallbackDelegate(callback, handler);
497             }
498         }
499 
500         @Override // Binder call
onPaused()501         public void onPaused() {
502             if (mDelegate != null) {
503                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED);
504             }
505         }
506 
507         @Override // Binder call
onResumed()508         public void onResumed() {
509             if (mDelegate != null) {
510                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED);
511             }
512         }
513 
514         @Override // Binder call
onStopped()515         public void onStopped() {
516             if (mDelegate != null) {
517                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED);
518             }
519         }
520     }
521 
522     private final static class VirtualDisplayCallbackDelegate extends Handler {
523         public static final int MSG_DISPLAY_PAUSED = 0;
524         public static final int MSG_DISPLAY_RESUMED = 1;
525         public static final int MSG_DISPLAY_STOPPED = 2;
526 
527         private final VirtualDisplay.Callback mCallback;
528 
VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback, Handler handler)529         public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback,
530                 Handler handler) {
531             super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
532             mCallback = callback;
533         }
534 
535         @Override
handleMessage(Message msg)536         public void handleMessage(Message msg) {
537             switch (msg.what) {
538                 case MSG_DISPLAY_PAUSED:
539                     mCallback.onPaused();
540                     break;
541                 case MSG_DISPLAY_RESUMED:
542                     mCallback.onResumed();
543                     break;
544                 case MSG_DISPLAY_STOPPED:
545                     mCallback.onStopped();
546                     break;
547             }
548         }
549     }
550 }
551