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