DisplayManagerGlobal.java revision a506a6ec94863a35acca9feb165db76ddac3892c
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
17package android.hardware.display;
18
19import android.content.Context;
20import android.hardware.display.DisplayManager.DisplayListener;
21import android.os.Binder;
22import android.os.Handler;
23import android.os.IBinder;
24import android.os.Looper;
25import android.os.Message;
26import android.os.RemoteException;
27import android.os.ServiceManager;
28import android.text.TextUtils;
29import android.util.Log;
30import android.util.SparseArray;
31import android.view.CompatibilityInfoHolder;
32import android.view.Display;
33import android.view.DisplayInfo;
34import android.view.Surface;
35
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
48    // 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
58    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) {
107                DisplayInfo info;
108                if (USE_CACHE) {
109                    info = mDisplayInfoCache.get(displayId);
110                    if (info != null) {
111                        return info;
112                    }
113                }
114
115                info = mDm.getDisplayInfo(displayId);
116                if (info == null) {
117                    return null;
118                }
119
120                if (USE_CACHE) {
121                    mDisplayInfoCache.put(displayId, info);
122                }
123                registerCallbackIfNeededLocked();
124
125                if (DEBUG) {
126                    Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
127                }
128                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) {
144                if (USE_CACHE) {
145                    if (mDisplayIdCache != null) {
146                        return mDisplayIdCache;
147                    }
148                }
149
150                int[] displayIds = mDm.getDisplayIds();
151                if (USE_CACHE) {
152                    mDisplayIdCache = displayIds;
153                }
154                registerCallbackIfNeededLocked();
155                return displayIds;
156            }
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) {
244            if (USE_CACHE) {
245                mDisplayInfoCache.remove(displayId);
246
247                if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
248                    mDisplayIdCache = null;
249                }
250            }
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
259    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
287    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
312    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
321    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
368    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}
411