blob: a8586814af6ede507c1677f8e94507ccb271b90d [file] [log] [blame]
Jeff Brownbd6e1502012-08-28 03:27:37 -07001/*
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
17package android.hardware.display;
18
19import android.content.Context;
20import android.hardware.display.DisplayManager.DisplayListener;
21import android.os.Handler;
22import android.os.IBinder;
23import android.os.Looper;
24import android.os.Message;
25import android.os.RemoteException;
26import android.os.ServiceManager;
27import android.util.Log;
28import android.util.SparseArray;
29import android.view.CompatibilityInfoHolder;
30import android.view.Display;
31import android.view.DisplayInfo;
32
33import 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 */
41public final class DisplayManagerGlobal {
42 private static final String TAG = "DisplayManager";
43 private static final boolean DEBUG = false;
44
Jeff Brown4ed8fe72012-08-30 18:18:29 -070045 // 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
Jeff Brownbd6e1502012-08-28 03:27:37 -070055 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
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 */
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 */
101 public DisplayInfo getDisplayInfo(int displayId) {
102 try {
103 synchronized (mLock) {
Jeff Brown4ed8fe72012-08-30 18:18:29 -0700104 DisplayInfo info;
105 if (USE_CACHE) {
106 info = mDisplayInfoCache.get(displayId);
107 if (info != null) {
108 return info;
109 }
Jeff Brownbd6e1502012-08-28 03:27:37 -0700110 }
111
112 info = mDm.getDisplayInfo(displayId);
113 if (info == null) {
114 return null;
115 }
Jeff Brown4ed8fe72012-08-30 18:18:29 -0700116
117 if (USE_CACHE) {
118 mDisplayInfoCache.put(displayId, info);
119 }
120 registerCallbackIfNeededLocked();
121
Jeff Brownbd6e1502012-08-28 03:27:37 -0700122 if (DEBUG) {
123 Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
124 }
Jeff Brownbd6e1502012-08-28 03:27:37 -0700125 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 */
138 public int[] getDisplayIds() {
139 try {
140 synchronized (mLock) {
Jeff Brown4ed8fe72012-08-30 18:18:29 -0700141 if (USE_CACHE) {
142 if (mDisplayIdCache != null) {
143 return mDisplayIdCache;
144 }
Jeff Brownbd6e1502012-08-28 03:27:37 -0700145 }
Jeff Brown4ed8fe72012-08-30 18:18:29 -0700146
147 int[] displayIds = mDm.getDisplayIds();
148 if (USE_CACHE) {
149 mDisplayIdCache = displayIds;
150 }
151 registerCallbackIfNeededLocked();
152 return displayIds;
Jeff Brownbd6e1502012-08-28 03:27:37 -0700153 }
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 */
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 */
184 public Display getRealDisplay(int displayId) {
185 return getCompatibleDisplay(displayId, null);
186 }
187
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
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
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
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
239 private void handleDisplayEvent(int displayId, int event) {
240 synchronized (mLock) {
Jeff Brown4ed8fe72012-08-30 18:18:29 -0700241 if (USE_CACHE) {
242 mDisplayInfoCache.remove(displayId);
Jeff Brownbd6e1502012-08-28 03:27:37 -0700243
Jeff Brown4ed8fe72012-08-30 18:18:29 -0700244 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
245 mDisplayIdCache = null;
246 }
Jeff Brownbd6e1502012-08-28 03:27:37 -0700247 }
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
Jeff Browne08ae382012-09-07 20:36:36 -0700256 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
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
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
Jeff Brown89d55462012-09-19 11:33:42 -0700284 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
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
Jeff Browne08ae382012-09-07 20:36:36 -0700309 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
Jeff Brownbd6e1502012-08-28 03:27:37 -0700318 private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
319 @Override
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
331 public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
332 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
333 mListener = listener;
334 }
335
336 public void sendDisplayEvent(int displayId, int event) {
337 Message msg = obtainMessage(event, displayId, 0);
338 sendMessage(msg);
339 }
340
341 public void clearEvents() {
342 removeCallbacksAndMessages(null);
343 }
344
345 @Override
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}