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