DisplayManager.java revision 48d0d1886731ff19ed3fb47a5997be5df0d1bba8
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.os.Handler;
21import android.util.SparseArray;
22import android.view.Display;
23import android.view.Surface;
24
25import java.util.ArrayList;
26
27/**
28 * Manages the properties of attached displays.
29 * <p>
30 * Get an instance of this class by calling
31 * {@link android.content.Context#getSystemService(java.lang.String)
32 * Context.getSystemService()} with the argument
33 * {@link android.content.Context#DISPLAY_SERVICE}.
34 * </p>
35 */
36public final class DisplayManager {
37    private static final String TAG = "DisplayManager";
38    private static final boolean DEBUG = false;
39
40    private final Context mContext;
41    private final DisplayManagerGlobal mGlobal;
42
43    private final Object mLock = new Object();
44    private final SparseArray<Display> mDisplays = new SparseArray<Display>();
45
46    private final ArrayList<Display> mTempDisplays = new ArrayList<Display>();
47
48    /**
49     * Broadcast receiver that indicates when the Wifi display status changes.
50     * <p>
51     * The status is provided as a {@link WifiDisplayStatus} object in the
52     * {@link #EXTRA_WIFI_DISPLAY_STATUS} extra.
53     * </p><p>
54     * This broadcast is only sent to registered receivers and can only be sent by the system.
55     * </p>
56     * @hide
57     */
58    public static final String ACTION_WIFI_DISPLAY_STATUS_CHANGED =
59            "android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED";
60
61    /**
62     * Contains a {@link WifiDisplayStatus} object.
63     * @hide
64     */
65    public static final String EXTRA_WIFI_DISPLAY_STATUS =
66            "android.hardware.display.extra.WIFI_DISPLAY_STATUS";
67
68    /**
69     * Display category: Presentation displays.
70     * <p>
71     * This category can be used to identify secondary displays that are suitable for
72     * use as presentation displays.
73     * </p>
74     *
75     * @see android.app.Presentation for information about presenting content
76     * on secondary displays.
77     * @see #getDisplays(String)
78     */
79    public static final String DISPLAY_CATEGORY_PRESENTATION =
80            "android.hardware.display.category.PRESENTATION";
81
82    /** @hide */
83    public DisplayManager(Context context) {
84        mContext = context;
85        mGlobal = DisplayManagerGlobal.getInstance();
86    }
87
88    /**
89     * Gets information about a logical display.
90     *
91     * The display metrics may be adjusted to provide compatibility
92     * for legacy applications.
93     *
94     * @param displayId The logical display id.
95     * @return The display object, or null if there is no valid display with the given id.
96     */
97    public Display getDisplay(int displayId) {
98        synchronized (mLock) {
99            return getOrCreateDisplayLocked(displayId, false /*assumeValid*/);
100        }
101    }
102
103    /**
104     * Gets all currently valid logical displays.
105     *
106     * @return An array containing all displays.
107     */
108    public Display[] getDisplays() {
109        return getDisplays(null);
110    }
111
112    /**
113     * Gets all currently valid logical displays of the specified category.
114     * <p>
115     * When there are multiple displays in a category the returned displays are sorted
116     * of preference.  For example, if the requested category is
117     * {@link #DISPLAY_CATEGORY_PRESENTATION} and there are multiple presentation displays
118     * then the displays are sorted so that the first display in the returned array
119     * is the most preferred presentation display.  The application may simply
120     * use the first display or allow the user to choose.
121     * </p>
122     *
123     * @param category The requested display category or null to return all displays.
124     * @return An array containing all displays sorted by order of preference.
125     *
126     * @see #DISPLAY_CATEGORY_PRESENTATION
127     */
128    public Display[] getDisplays(String category) {
129        final int[] displayIds = mGlobal.getDisplayIds();
130        synchronized (mLock) {
131            try {
132                if (category == null) {
133                    addMatchingDisplaysLocked(mTempDisplays, displayIds, -1);
134                } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
135                    addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
136                    addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_HDMI);
137                    addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
138                    addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
139                }
140                return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
141            } finally {
142                mTempDisplays.clear();
143            }
144        }
145    }
146
147    private void addMatchingDisplaysLocked(
148            ArrayList<Display> displays, int[] displayIds, int matchType) {
149        for (int i = 0; i < displayIds.length; i++) {
150            Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
151            if (display != null
152                    && (matchType < 0 || display.getType() == matchType)) {
153                displays.add(display);
154            }
155        }
156    }
157
158    private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {
159        Display display = mDisplays.get(displayId);
160        if (display == null) {
161            display = mGlobal.getCompatibleDisplay(displayId,
162                    mContext.getDisplayAdjustments(displayId));
163            if (display != null) {
164                mDisplays.put(displayId, display);
165            }
166        } else if (!assumeValid && !display.isValid()) {
167            display = null;
168        }
169        return display;
170    }
171
172    /**
173     * Registers an display listener to receive notifications about when
174     * displays are added, removed or changed.
175     *
176     * @param listener The listener to register.
177     * @param handler The handler on which the listener should be invoked, or null
178     * if the listener should be invoked on the calling thread's looper.
179     *
180     * @see #unregisterDisplayListener
181     */
182    public void registerDisplayListener(DisplayListener listener, Handler handler) {
183        mGlobal.registerDisplayListener(listener, handler);
184    }
185
186    /**
187     * Unregisters an input device listener.
188     *
189     * @param listener The listener to unregister.
190     *
191     * @see #registerDisplayListener
192     */
193    public void unregisterDisplayListener(DisplayListener listener) {
194        mGlobal.unregisterDisplayListener(listener);
195    }
196
197    /**
198     * Initiates a fresh scan of availble Wifi displays.
199     * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast.
200     * @hide
201     */
202    public void scanWifiDisplays() {
203        mGlobal.scanWifiDisplays();
204    }
205
206    /**
207     * Connects to a Wifi display.
208     * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast.
209     * <p>
210     * Automatically remembers the display after a successful connection, if not
211     * already remembered.
212     * </p><p>
213     * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY} to connect
214     * to unknown displays.  No permissions are required to connect to already known displays.
215     * </p>
216     *
217     * @param deviceAddress The MAC address of the device to which we should connect.
218     * @hide
219     */
220    public void connectWifiDisplay(String deviceAddress) {
221        mGlobal.connectWifiDisplay(deviceAddress);
222    }
223
224    /**
225     * Disconnects from the current Wifi display.
226     * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast.
227     * @hide
228     */
229    public void disconnectWifiDisplay() {
230        mGlobal.disconnectWifiDisplay();
231    }
232
233    /**
234     * Renames a Wifi display.
235     * <p>
236     * The display must already be remembered for this call to succeed.  In other words,
237     * we must already have successfully connected to the display at least once and then
238     * not forgotten it.
239     * </p><p>
240     * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}.
241     * </p>
242     *
243     * @param deviceAddress The MAC address of the device to rename.
244     * @param alias The alias name by which to remember the device, or null
245     * or empty if no alias should be used.
246     * @hide
247     */
248    public void renameWifiDisplay(String deviceAddress, String alias) {
249        mGlobal.renameWifiDisplay(deviceAddress, alias);
250    }
251
252    /**
253     * Forgets a previously remembered Wifi display.
254     * <p>
255     * Automatically disconnects from the display if currently connected to it.
256     * </p><p>
257     * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}.
258     * </p>
259     *
260     * @param deviceAddress The MAC address of the device to forget.
261     * @hide
262     */
263    public void forgetWifiDisplay(String deviceAddress) {
264        mGlobal.forgetWifiDisplay(deviceAddress);
265    }
266
267    /**
268     * Gets the current Wifi display status.
269     * Watch for changes in the status by registering a broadcast receiver for
270     * {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED}.
271     *
272     * @return The current Wifi display status.
273     * @hide
274     */
275    public WifiDisplayStatus getWifiDisplayStatus() {
276        return mGlobal.getWifiDisplayStatus();
277    }
278
279    /**
280     * Creates a private virtual display.
281     * <p>
282     * The content of a virtual display is rendered to a {@link Surface} provided
283     * by the application that created the virtual display.
284     * </p><p>
285     * Only the application that created a private virtual display is allowed to
286     * place windows upon it.  The private virtual display also does not participate
287     * in display mirroring: it will neither receive mirrored content from another
288     * display nor allow its own content to be mirrored elsewhere.  More precisely,
289     * the only processes that are allowed to enumerate or interact with a private
290     * display are those that have the same UID as the application that originally
291     * created the private virtual display.
292     * </p><p>
293     * The private virtual display should be {@link VirtualDisplay#release released}
294     * when no longer needed.  Because a private virtual display renders to a surface
295     * provided by the application, it will be released automatically when the
296     * process terminates and all remaining windows on it will be forcibly removed.
297     * </p>
298     *
299     * @param name The name of the virtual display, must be non-empty.
300     * @param width The width of the virtual display in pixels, must be greater than 0.
301     * @param height The height of the virtual display in pixels, must be greater than 0.
302     * @param densityDpi The density of the virtual display in dpi, must be greater than 0.
303     * @param surface The surface to which the content of the virtual display should
304     * be rendered, must be non-null.
305     * @return The newly created virtual display, or null if the application could
306     * not create the virtual display.
307     */
308    public VirtualDisplay createPrivateVirtualDisplay(String name,
309            int width, int height, int densityDpi, Surface surface) {
310        return mGlobal.createPrivateVirtualDisplay(mContext,
311                name, width, height, densityDpi, surface);
312    }
313
314    /**
315     * Listens for changes in available display devices.
316     */
317    public interface DisplayListener {
318        /**
319         * Called whenever a logical display has been added to the system.
320         * Use {@link DisplayManager#getDisplay} to get more information about
321         * the display.
322         *
323         * @param displayId The id of the logical display that was added.
324         */
325        void onDisplayAdded(int displayId);
326
327        /**
328         * Called whenever a logical display has been removed from the system.
329         *
330         * @param displayId The id of the logical display that was removed.
331         */
332        void onDisplayRemoved(int displayId);
333
334        /**
335         * Called whenever the properties of a logical display have changed.
336         *
337         * @param displayId The id of the logical display that changed.
338         */
339        void onDisplayChanged(int displayId);
340    }
341}
342