• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.hardware.display.DisplayManager.DisplayListener;
21 import android.os.Handler;
22 import android.os.IBinder;
23 import android.os.Looper;
24 import android.os.Message;
25 import android.os.RemoteException;
26 import android.os.ServiceManager;
27 import android.util.Log;
28 import android.util.SparseArray;
29 import android.view.CompatibilityInfoHolder;
30 import android.view.Display;
31 import android.view.DisplayInfo;
32 
33 import java.util.ArrayList;
34 
35 /**
36  * Manager communication with the display manager service on behalf of
37  * an application process.  You're probably looking for {@link DisplayManager}.
38  *
39  * @hide
40  */
41 public final class DisplayManagerGlobal {
42     private static final String TAG = "DisplayManager";
43     private static final boolean DEBUG = false;
44 
45     // True if display info and display ids should be cached.
46     //
47     // FIXME: The cache is currently disabled because it's unclear whether we have the
48     // necessary guarantees that the caches will always be flushed before clients
49     // attempt to observe their new state.  For example, depending on the order
50     // in which the binder transactions take place, we might have a problem where
51     // an application could start processing a configuration change due to a display
52     // orientation change before the display info cache has actually been invalidated.
53     private static final boolean USE_CACHE = false;
54 
55     public static final int EVENT_DISPLAY_ADDED = 1;
56     public static final int EVENT_DISPLAY_CHANGED = 2;
57     public static final int EVENT_DISPLAY_REMOVED = 3;
58 
59     private static DisplayManagerGlobal sInstance;
60 
61     private final Object mLock = new Object();
62 
63     private final IDisplayManager mDm;
64 
65     private DisplayManagerCallback mCallback;
66     private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
67             new ArrayList<DisplayListenerDelegate>();
68 
69     private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>();
70     private int[] mDisplayIdCache;
71 
DisplayManagerGlobal(IDisplayManager dm)72     private DisplayManagerGlobal(IDisplayManager dm) {
73         mDm = dm;
74     }
75 
76     /**
77      * Gets an instance of the display manager global singleton.
78      *
79      * @return The display manager instance, may be null early in system startup
80      * before the display manager has been fully initialized.
81      */
getInstance()82     public static DisplayManagerGlobal getInstance() {
83         synchronized (DisplayManagerGlobal.class) {
84             if (sInstance == null) {
85                 IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
86                 if (b != null) {
87                     sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
88                 }
89             }
90             return sInstance;
91         }
92     }
93 
94     /**
95      * Get information about a particular logical display.
96      *
97      * @param displayId The logical display id.
98      * @return Information about the specified display, or null if it does not exist.
99      * This object belongs to an internal cache and should be treated as if it were immutable.
100      */
getDisplayInfo(int displayId)101     public DisplayInfo getDisplayInfo(int displayId) {
102         try {
103             synchronized (mLock) {
104                 DisplayInfo info;
105                 if (USE_CACHE) {
106                     info = mDisplayInfoCache.get(displayId);
107                     if (info != null) {
108                         return info;
109                     }
110                 }
111 
112                 info = mDm.getDisplayInfo(displayId);
113                 if (info == null) {
114                     return null;
115                 }
116 
117                 if (USE_CACHE) {
118                     mDisplayInfoCache.put(displayId, info);
119                 }
120                 registerCallbackIfNeededLocked();
121 
122                 if (DEBUG) {
123                     Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
124                 }
125                 return info;
126             }
127         } catch (RemoteException ex) {
128             Log.e(TAG, "Could not get display information from display manager.", ex);
129             return null;
130         }
131     }
132 
133     /**
134      * Gets all currently valid logical display ids.
135      *
136      * @return An array containing all display ids.
137      */
getDisplayIds()138     public int[] getDisplayIds() {
139         try {
140             synchronized (mLock) {
141                 if (USE_CACHE) {
142                     if (mDisplayIdCache != null) {
143                         return mDisplayIdCache;
144                     }
145                 }
146 
147                 int[] displayIds = mDm.getDisplayIds();
148                 if (USE_CACHE) {
149                     mDisplayIdCache = displayIds;
150                 }
151                 registerCallbackIfNeededLocked();
152                 return displayIds;
153             }
154         } catch (RemoteException ex) {
155             Log.e(TAG, "Could not get display ids from display manager.", ex);
156             return new int[] { Display.DEFAULT_DISPLAY };
157         }
158     }
159 
160     /**
161      * Gets information about a logical display.
162      *
163      * The display metrics may be adjusted to provide compatibility
164      * for legacy applications.
165      *
166      * @param displayId The logical display id.
167      * @param cih The compatibility info, or null if none is required.
168      * @return The display object, or null if there is no display with the given id.
169      */
getCompatibleDisplay(int displayId, CompatibilityInfoHolder cih)170     public Display getCompatibleDisplay(int displayId, CompatibilityInfoHolder cih) {
171         DisplayInfo displayInfo = getDisplayInfo(displayId);
172         if (displayInfo == null) {
173             return null;
174         }
175         return new Display(this, displayId, displayInfo, cih);
176     }
177 
178     /**
179      * Gets information about a logical display without applying any compatibility metrics.
180      *
181      * @param displayId The logical display id.
182      * @return The display object, or null if there is no display with the given id.
183      */
getRealDisplay(int displayId)184     public Display getRealDisplay(int displayId) {
185         return getCompatibleDisplay(displayId, null);
186     }
187 
registerDisplayListener(DisplayListener listener, Handler handler)188     public void registerDisplayListener(DisplayListener listener, Handler handler) {
189         if (listener == null) {
190             throw new IllegalArgumentException("listener must not be null");
191         }
192 
193         synchronized (mLock) {
194             int index = findDisplayListenerLocked(listener);
195             if (index < 0) {
196                 mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
197                 registerCallbackIfNeededLocked();
198             }
199         }
200     }
201 
unregisterDisplayListener(DisplayListener listener)202     public void unregisterDisplayListener(DisplayListener listener) {
203         if (listener == null) {
204             throw new IllegalArgumentException("listener must not be null");
205         }
206 
207         synchronized (mLock) {
208             int index = findDisplayListenerLocked(listener);
209             if (index >= 0) {
210                 DisplayListenerDelegate d = mDisplayListeners.get(index);
211                 d.clearEvents();
212                 mDisplayListeners.remove(index);
213             }
214         }
215     }
216 
findDisplayListenerLocked(DisplayListener listener)217     private int findDisplayListenerLocked(DisplayListener listener) {
218         final int numListeners = mDisplayListeners.size();
219         for (int i = 0; i < numListeners; i++) {
220             if (mDisplayListeners.get(i).mListener == listener) {
221                 return i;
222             }
223         }
224         return -1;
225     }
226 
registerCallbackIfNeededLocked()227     private void registerCallbackIfNeededLocked() {
228         if (mCallback == null) {
229             mCallback = new DisplayManagerCallback();
230             try {
231                 mDm.registerCallback(mCallback);
232             } catch (RemoteException ex) {
233                 Log.e(TAG, "Failed to register callback with display manager service.", ex);
234                 mCallback = null;
235             }
236         }
237     }
238 
handleDisplayEvent(int displayId, int event)239     private void handleDisplayEvent(int displayId, int event) {
240         synchronized (mLock) {
241             if (USE_CACHE) {
242                 mDisplayInfoCache.remove(displayId);
243 
244                 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
245                     mDisplayIdCache = null;
246                 }
247             }
248 
249             final int numListeners = mDisplayListeners.size();
250             for (int i = 0; i < numListeners; i++) {
251                 mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
252             }
253         }
254     }
255 
scanWifiDisplays()256     public void scanWifiDisplays() {
257         try {
258             mDm.scanWifiDisplays();
259         } catch (RemoteException ex) {
260             Log.e(TAG, "Failed to scan for Wifi displays.", ex);
261         }
262     }
263 
connectWifiDisplay(String deviceAddress)264     public void connectWifiDisplay(String deviceAddress) {
265         if (deviceAddress == null) {
266             throw new IllegalArgumentException("deviceAddress must not be null");
267         }
268 
269         try {
270             mDm.connectWifiDisplay(deviceAddress);
271         } catch (RemoteException ex) {
272             Log.e(TAG, "Failed to connect to Wifi display " + deviceAddress + ".", ex);
273         }
274     }
275 
disconnectWifiDisplay()276     public void disconnectWifiDisplay() {
277         try {
278             mDm.disconnectWifiDisplay();
279         } catch (RemoteException ex) {
280             Log.e(TAG, "Failed to disconnect from Wifi display.", ex);
281         }
282     }
283 
renameWifiDisplay(String deviceAddress, String alias)284     public void renameWifiDisplay(String deviceAddress, String alias) {
285         if (deviceAddress == null) {
286             throw new IllegalArgumentException("deviceAddress must not be null");
287         }
288 
289         try {
290             mDm.renameWifiDisplay(deviceAddress, alias);
291         } catch (RemoteException ex) {
292             Log.e(TAG, "Failed to rename Wifi display " + deviceAddress
293                     + " with alias " + alias + ".", ex);
294         }
295     }
296 
forgetWifiDisplay(String deviceAddress)297     public void forgetWifiDisplay(String deviceAddress) {
298         if (deviceAddress == null) {
299             throw new IllegalArgumentException("deviceAddress must not be null");
300         }
301 
302         try {
303             mDm.forgetWifiDisplay(deviceAddress);
304         } catch (RemoteException ex) {
305             Log.e(TAG, "Failed to forget Wifi display.", ex);
306         }
307     }
308 
getWifiDisplayStatus()309     public WifiDisplayStatus getWifiDisplayStatus() {
310         try {
311             return mDm.getWifiDisplayStatus();
312         } catch (RemoteException ex) {
313             Log.e(TAG, "Failed to get Wifi display status.", ex);
314             return new WifiDisplayStatus();
315         }
316     }
317 
318     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
319         @Override
onDisplayEvent(int displayId, int event)320         public void onDisplayEvent(int displayId, int event) {
321             if (DEBUG) {
322                 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
323             }
324             handleDisplayEvent(displayId, event);
325         }
326     }
327 
328     private static final class DisplayListenerDelegate extends Handler {
329         public final DisplayListener mListener;
330 
DisplayListenerDelegate(DisplayListener listener, Handler handler)331         public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
332             super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
333             mListener = listener;
334         }
335 
sendDisplayEvent(int displayId, int event)336         public void sendDisplayEvent(int displayId, int event) {
337             Message msg = obtainMessage(event, displayId, 0);
338             sendMessage(msg);
339         }
340 
clearEvents()341         public void clearEvents() {
342             removeCallbacksAndMessages(null);
343         }
344 
345         @Override
handleMessage(Message msg)346         public void handleMessage(Message msg) {
347             switch (msg.what) {
348                 case EVENT_DISPLAY_ADDED:
349                     mListener.onDisplayAdded(msg.arg1);
350                     break;
351                 case EVENT_DISPLAY_CHANGED:
352                     mListener.onDisplayChanged(msg.arg1);
353                     break;
354                 case EVENT_DISPLAY_REMOVED:
355                     mListener.onDisplayRemoved(msg.arg1);
356                     break;
357             }
358         }
359     }
360 }
361