blob: 320185d906d8d487e503693c88d3c1831cc3c299 [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;
Craig Mautner48d0d182013-06-11 07:53:06 -070031import android.view.DisplayAdjustments;
Jeff Brownbd6e1502012-08-28 03:27:37 -070032import 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
Craig Mautner48d0d182013-06-11 07:53:06 -0700167 * for legacy applications or limited screen areas.
Jeff Brownbd6e1502012-08-28 03:27:37 -0700168 *
169 * @param displayId The logical display id.
Craig Mautner48d0d182013-06-11 07:53:06 -0700170 * @param daj The compatibility info and activityToken.
Jeff Brownbd6e1502012-08-28 03:27:37 -0700171 * @return The display object, or null if there is no display with the given id.
172 */
Craig Mautner48d0d182013-06-11 07:53:06 -0700173 public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
Jeff Brownbd6e1502012-08-28 03:27:37 -0700174 DisplayInfo displayInfo = getDisplayInfo(displayId);
175 if (displayInfo == null) {
176 return null;
177 }
Craig Mautner48d0d182013-06-11 07:53:06 -0700178 return new Display(this, displayId, displayInfo, daj);
Jeff Brownbd6e1502012-08-28 03:27:37 -0700179 }
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) {
Craig Mautner48d0d182013-06-11 07:53:06 -0700188 return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
189 }
190
191 /**
192 * Gets information about a logical display without applying any compatibility metrics.
193 *
194 * @param displayId The logical display id.
195 * @param IBinder the activity token for this display.
196 * @return The display object, or null if there is no display with the given id.
197 */
198 public Display getRealDisplay(int displayId, IBinder token) {
199 return getCompatibleDisplay(displayId, new DisplayAdjustments(token));
Jeff Brownbd6e1502012-08-28 03:27:37 -0700200 }
201
202 public void registerDisplayListener(DisplayListener listener, Handler handler) {
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 mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
211 registerCallbackIfNeededLocked();
212 }
213 }
214 }
215
216 public void unregisterDisplayListener(DisplayListener listener) {
217 if (listener == null) {
218 throw new IllegalArgumentException("listener must not be null");
219 }
220
221 synchronized (mLock) {
222 int index = findDisplayListenerLocked(listener);
223 if (index >= 0) {
224 DisplayListenerDelegate d = mDisplayListeners.get(index);
225 d.clearEvents();
226 mDisplayListeners.remove(index);
227 }
228 }
229 }
230
231 private int findDisplayListenerLocked(DisplayListener listener) {
232 final int numListeners = mDisplayListeners.size();
233 for (int i = 0; i < numListeners; i++) {
234 if (mDisplayListeners.get(i).mListener == listener) {
235 return i;
236 }
237 }
238 return -1;
239 }
240
241 private void registerCallbackIfNeededLocked() {
242 if (mCallback == null) {
243 mCallback = new DisplayManagerCallback();
244 try {
245 mDm.registerCallback(mCallback);
246 } catch (RemoteException ex) {
247 Log.e(TAG, "Failed to register callback with display manager service.", ex);
248 mCallback = null;
249 }
250 }
251 }
252
253 private void handleDisplayEvent(int displayId, int event) {
254 synchronized (mLock) {
Jeff Brown4ed8fe72012-08-30 18:18:29 -0700255 if (USE_CACHE) {
256 mDisplayInfoCache.remove(displayId);
Jeff Brownbd6e1502012-08-28 03:27:37 -0700257
Jeff Brown4ed8fe72012-08-30 18:18:29 -0700258 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
259 mDisplayIdCache = null;
260 }
Jeff Brownbd6e1502012-08-28 03:27:37 -0700261 }
262
263 final int numListeners = mDisplayListeners.size();
264 for (int i = 0; i < numListeners; i++) {
265 mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
266 }
267 }
268 }
269
Jeff Browne08ae382012-09-07 20:36:36 -0700270 public void scanWifiDisplays() {
271 try {
272 mDm.scanWifiDisplays();
273 } catch (RemoteException ex) {
274 Log.e(TAG, "Failed to scan for Wifi displays.", ex);
275 }
276 }
277
278 public void connectWifiDisplay(String deviceAddress) {
279 if (deviceAddress == null) {
280 throw new IllegalArgumentException("deviceAddress must not be null");
281 }
282
283 try {
284 mDm.connectWifiDisplay(deviceAddress);
285 } catch (RemoteException ex) {
286 Log.e(TAG, "Failed to connect to Wifi display " + deviceAddress + ".", ex);
287 }
288 }
289
290 public void disconnectWifiDisplay() {
291 try {
292 mDm.disconnectWifiDisplay();
293 } catch (RemoteException ex) {
294 Log.e(TAG, "Failed to disconnect from Wifi display.", ex);
295 }
296 }
297
Jeff Brown89d55462012-09-19 11:33:42 -0700298 public void renameWifiDisplay(String deviceAddress, String alias) {
299 if (deviceAddress == null) {
300 throw new IllegalArgumentException("deviceAddress must not be null");
301 }
302
303 try {
304 mDm.renameWifiDisplay(deviceAddress, alias);
305 } catch (RemoteException ex) {
306 Log.e(TAG, "Failed to rename Wifi display " + deviceAddress
307 + " with alias " + alias + ".", ex);
308 }
309 }
310
311 public void forgetWifiDisplay(String deviceAddress) {
312 if (deviceAddress == null) {
313 throw new IllegalArgumentException("deviceAddress must not be null");
314 }
315
316 try {
317 mDm.forgetWifiDisplay(deviceAddress);
318 } catch (RemoteException ex) {
319 Log.e(TAG, "Failed to forget Wifi display.", ex);
320 }
321 }
322
Jeff Browne08ae382012-09-07 20:36:36 -0700323 public WifiDisplayStatus getWifiDisplayStatus() {
324 try {
325 return mDm.getWifiDisplayStatus();
326 } catch (RemoteException ex) {
327 Log.e(TAG, "Failed to get Wifi display status.", ex);
328 return new WifiDisplayStatus();
329 }
330 }
331
Jeff Browna506a6e2013-06-04 00:02:38 -0700332 public VirtualDisplay createPrivateVirtualDisplay(Context context, String name,
333 int width, int height, int densityDpi, Surface surface) {
334 if (TextUtils.isEmpty(name)) {
335 throw new IllegalArgumentException("name must be non-null and non-empty");
336 }
337 if (width <= 0 || height <= 0 || densityDpi <= 0) {
338 throw new IllegalArgumentException("width, height, and densityDpi must be "
339 + "greater than 0");
340 }
341 if (surface == null) {
342 throw new IllegalArgumentException("surface must not be null");
343 }
344
345 Binder token = new Binder();
346 int displayId;
347 try {
348 displayId = mDm.createPrivateVirtualDisplay(token, context.getPackageName(),
349 name, width, height, densityDpi, surface);
350 } catch (RemoteException ex) {
351 Log.e(TAG, "Could not create private virtual display: " + name, ex);
352 return null;
353 }
354 if (displayId < 0) {
355 Log.e(TAG, "Could not create private virtual display: " + name);
356 return null;
357 }
358 Display display = getRealDisplay(displayId);
359 if (display == null) {
360 Log.wtf(TAG, "Could not obtain display info for newly created "
361 + "private virtual display: " + name);
362 try {
363 mDm.releaseVirtualDisplay(token);
364 } catch (RemoteException ex) {
365 }
366 return null;
367 }
368 return new VirtualDisplay(this, display, token);
369 }
370
371 public void releaseVirtualDisplay(IBinder token) {
372 try {
373 mDm.releaseVirtualDisplay(token);
374 } catch (RemoteException ex) {
375 Log.w(TAG, "Failed to release virtual display.", ex);
376 }
377 }
378
Jeff Brownbd6e1502012-08-28 03:27:37 -0700379 private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
380 @Override
381 public void onDisplayEvent(int displayId, int event) {
382 if (DEBUG) {
383 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
384 }
385 handleDisplayEvent(displayId, event);
386 }
387 }
388
389 private static final class DisplayListenerDelegate extends Handler {
390 public final DisplayListener mListener;
391
392 public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
393 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
394 mListener = listener;
395 }
396
397 public void sendDisplayEvent(int displayId, int event) {
398 Message msg = obtainMessage(event, displayId, 0);
399 sendMessage(msg);
400 }
401
402 public void clearEvents() {
403 removeCallbacksAndMessages(null);
404 }
405
406 @Override
407 public void handleMessage(Message msg) {
408 switch (msg.what) {
409 case EVENT_DISPLAY_ADDED:
410 mListener.onDisplayAdded(msg.arg1);
411 break;
412 case EVENT_DISPLAY_CHANGED:
413 mListener.onDisplayChanged(msg.arg1);
414 break;
415 case EVENT_DISPLAY_REMOVED:
416 mListener.onDisplayRemoved(msg.arg1);
417 break;
418 }
419 }
420 }
421}