2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.hardware.display;
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;
38 import java.util.ArrayList;
41 * Manager communication with the display manager service on behalf of
42 * an application process. You're probably looking for {@link DisplayManager}.
46 public final class DisplayManagerGlobal {
47 private static final String TAG = "DisplayManager";
48 private static final boolean DEBUG = false;
50 // True if display info and display ids should be cached.
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;
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;
64 private static DisplayManagerGlobal sInstance;
66 private final Object mLock = new Object();
68 private final IDisplayManager mDm;
70 private DisplayManagerCallback mCallback;
71 private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
72 new ArrayList<DisplayListenerDelegate>();
74 private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>();
75 private int[] mDisplayIdCache;
77 private int mWifiDisplayScanNestCount;
79 private DisplayManagerGlobal(IDisplayManager dm) {
84 * Gets an instance of the display manager global singleton.
86 * @return The display manager instance, may be null early in system startup
87 * before the display manager has been fully initialized.
89 public static DisplayManagerGlobal getInstance() {
90 synchronized (DisplayManagerGlobal.class) {
91 if (sInstance == null) {
92 IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
94 sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
102 * Get information about a particular logical display.
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.
108 public DisplayInfo getDisplayInfo(int displayId) {
110 synchronized (mLock) {
113 info = mDisplayInfoCache.get(displayId);
119 info = mDm.getDisplayInfo(displayId);
125 mDisplayInfoCache.put(displayId, info);
127 registerCallbackIfNeededLocked();
130 Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
134 } catch (RemoteException ex) {
135 throw ex.rethrowFromSystemServer();
140 * Gets all currently valid logical display ids.
142 * @return An array containing all display ids.
144 public int[] getDisplayIds() {
146 synchronized (mLock) {
148 if (mDisplayIdCache != null) {
149 return mDisplayIdCache;
153 int[] displayIds = mDm.getDisplayIds();
155 mDisplayIdCache = displayIds;
157 registerCallbackIfNeededLocked();
160 } catch (RemoteException ex) {
161 throw ex.rethrowFromSystemServer();
166 * Gets information about a logical display.
168 * The display metrics may be adjusted to provide compatibility
169 * for legacy applications or limited screen areas.
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.
175 public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
176 DisplayInfo displayInfo = getDisplayInfo(displayId);
177 if (displayInfo == null) {
180 return new Display(this, displayId, displayInfo, daj);
184 * Gets information about a logical display without applying any compatibility metrics.
186 * @param displayId The logical display id.
187 * @return The display object, or null if there is no display with the given id.
189 public Display getRealDisplay(int displayId) {
190 return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
193 public void registerDisplayListener(DisplayListener listener, Handler handler) {
194 if (listener == null) {
195 throw new IllegalArgumentException("listener must not be null");
198 synchronized (mLock) {
199 int index = findDisplayListenerLocked(listener);
201 mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
202 registerCallbackIfNeededLocked();
207 public void unregisterDisplayListener(DisplayListener listener) {
208 if (listener == null) {
209 throw new IllegalArgumentException("listener must not be null");
212 synchronized (mLock) {
213 int index = findDisplayListenerLocked(listener);
215 DisplayListenerDelegate d = mDisplayListeners.get(index);
217 mDisplayListeners.remove(index);
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) {
232 private void registerCallbackIfNeededLocked() {
233 if (mCallback == null) {
234 mCallback = new DisplayManagerCallback();
236 mDm.registerCallback(mCallback);
237 } catch (RemoteException ex) {
238 throw ex.rethrowFromSystemServer();
243 private void handleDisplayEvent(int displayId, int event) {
244 synchronized (mLock) {
246 mDisplayInfoCache.remove(displayId);
248 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
249 mDisplayIdCache = null;
253 final int numListeners = mDisplayListeners.size();
254 for (int i = 0; i < numListeners; i++) {
255 mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
260 public void startWifiDisplayScan() {
261 synchronized (mLock) {
262 if (mWifiDisplayScanNestCount++ == 0) {
263 registerCallbackIfNeededLocked();
265 mDm.startWifiDisplayScan();
266 } catch (RemoteException ex) {
267 throw ex.rethrowFromSystemServer();
273 public void stopWifiDisplayScan() {
274 synchronized (mLock) {
275 if (--mWifiDisplayScanNestCount == 0) {
277 mDm.stopWifiDisplayScan();
278 } catch (RemoteException ex) {
279 throw ex.rethrowFromSystemServer();
281 } else if (mWifiDisplayScanNestCount < 0) {
282 Log.wtf(TAG, "Wifi display scan nest count became negative: "
283 + mWifiDisplayScanNestCount);
284 mWifiDisplayScanNestCount = 0;
289 public void connectWifiDisplay(String deviceAddress) {
290 if (deviceAddress == null) {
291 throw new IllegalArgumentException("deviceAddress must not be null");
295 mDm.connectWifiDisplay(deviceAddress);
296 } catch (RemoteException ex) {
297 throw ex.rethrowFromSystemServer();
301 public void pauseWifiDisplay() {
303 mDm.pauseWifiDisplay();
304 } catch (RemoteException ex) {
305 throw ex.rethrowFromSystemServer();
309 public void resumeWifiDisplay() {
311 mDm.resumeWifiDisplay();
312 } catch (RemoteException ex) {
313 throw ex.rethrowFromSystemServer();
317 public void disconnectWifiDisplay() {
319 mDm.disconnectWifiDisplay();
320 } catch (RemoteException ex) {
321 throw ex.rethrowFromSystemServer();
325 public void renameWifiDisplay(String deviceAddress, String alias) {
326 if (deviceAddress == null) {
327 throw new IllegalArgumentException("deviceAddress must not be null");
331 mDm.renameWifiDisplay(deviceAddress, alias);
332 } catch (RemoteException ex) {
333 throw ex.rethrowFromSystemServer();
337 public void forgetWifiDisplay(String deviceAddress) {
338 if (deviceAddress == null) {
339 throw new IllegalArgumentException("deviceAddress must not be null");
343 mDm.forgetWifiDisplay(deviceAddress);
344 } catch (RemoteException ex) {
345 throw ex.rethrowFromSystemServer();
349 public WifiDisplayStatus getWifiDisplayStatus() {
351 return mDm.getWifiDisplayStatus();
352 } catch (RemoteException ex) {
353 throw ex.rethrowFromSystemServer();
357 public void requestColorTransform(int displayId, int colorTransformId) {
359 mDm.requestColorTransform(displayId, colorTransformId);
360 } catch (RemoteException ex) {
361 throw ex.rethrowFromSystemServer();
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");
371 if (width <= 0 || height <= 0 || densityDpi <= 0) {
372 throw new IllegalArgumentException("width, height, and densityDpi must be "
376 VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
377 IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
380 displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken,
381 context.getPackageName(), name, width, height, densityDpi, surface, flags);
382 } catch (RemoteException ex) {
383 throw ex.rethrowFromSystemServer();
386 Log.e(TAG, "Could not create virtual display: " + name);
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);
394 mDm.releaseVirtualDisplay(callbackWrapper);
395 } catch (RemoteException ex) {
396 throw ex.rethrowFromSystemServer();
400 return new VirtualDisplay(this, display, callbackWrapper, surface);
403 public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
405 mDm.setVirtualDisplaySurface(token, surface);
406 } catch (RemoteException ex) {
407 throw ex.rethrowFromSystemServer();
411 public void resizeVirtualDisplay(IVirtualDisplayCallback token,
412 int width, int height, int densityDpi) {
414 mDm.resizeVirtualDisplay(token, width, height, densityDpi);
415 } catch (RemoteException ex) {
416 throw ex.rethrowFromSystemServer();
420 public void releaseVirtualDisplay(IVirtualDisplayCallback token) {
422 mDm.releaseVirtualDisplay(token);
423 } catch (RemoteException ex) {
424 throw ex.rethrowFromSystemServer();
428 private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
430 public void onDisplayEvent(int displayId, int event) {
432 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
434 handleDisplayEvent(displayId, event);
438 private static final class DisplayListenerDelegate extends Handler {
439 public final DisplayListener mListener;
441 public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
442 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
443 mListener = listener;
446 public void sendDisplayEvent(int displayId, int event) {
447 Message msg = obtainMessage(event, displayId, 0);
451 public void clearEvents() {
452 removeCallbacksAndMessages(null);
456 public void handleMessage(Message msg) {
458 case EVENT_DISPLAY_ADDED:
459 mListener.onDisplayAdded(msg.arg1);
461 case EVENT_DISPLAY_CHANGED:
462 mListener.onDisplayChanged(msg.arg1);
464 case EVENT_DISPLAY_REMOVED:
465 mListener.onDisplayRemoved(msg.arg1);
471 private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
472 private VirtualDisplayCallbackDelegate mDelegate;
474 public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) {
475 if (callback != null) {
476 mDelegate = new VirtualDisplayCallbackDelegate(callback, handler);
480 @Override // Binder call
481 public void onPaused() {
482 if (mDelegate != null) {
483 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED);
487 @Override // Binder call
488 public void onResumed() {
489 if (mDelegate != null) {
490 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED);
494 @Override // Binder call
495 public void onStopped() {
496 if (mDelegate != null) {
497 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED);
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;
507 private final VirtualDisplay.Callback mCallback;
509 public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback,
511 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
512 mCallback = callback;
516 public void handleMessage(Message msg) {
518 case MSG_DISPLAY_PAUSED:
519 mCallback.onPaused();
521 case MSG_DISPLAY_RESUMED:
522 mCallback.onResumed();
524 case MSG_DISPLAY_STOPPED:
525 mCallback.onStopped();