DisplayManagerGlobal.java revision 462e29da9ba854eb3651dd9664b09a2852a05141
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.content.pm.ParceledListSlice;
21import android.content.res.Resources;
22import android.graphics.Point;
23import android.hardware.display.DisplayManager.DisplayListener;
24import android.media.projection.IMediaProjection;
25import android.media.projection.MediaProjection;
26import android.os.Handler;
27import android.os.IBinder;
28import android.os.Looper;
29import android.os.Message;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.text.TextUtils;
33import android.util.Log;
34import android.util.SparseArray;
35import android.view.Display;
36import android.view.DisplayAdjustments;
37import android.view.DisplayInfo;
38import android.view.Surface;
39
40import java.util.ArrayList;
41import java.util.Collections;
42import java.util.List;
43
44/**
45 * Manager communication with the display manager service on behalf of
46 * an application process.  You're probably looking for {@link DisplayManager}.
47 *
48 * @hide
49 */
50public final class DisplayManagerGlobal {
51    private static final String TAG = "DisplayManager";
52    private static final boolean DEBUG = false;
53
54    // True if display info and display ids should be cached.
55    //
56    // FIXME: The cache is currently disabled because it's unclear whether we have the
57    // necessary guarantees that the caches will always be flushed before clients
58    // attempt to observe their new state.  For example, depending on the order
59    // in which the binder transactions take place, we might have a problem where
60    // an application could start processing a configuration change due to a display
61    // orientation change before the display info cache has actually been invalidated.
62    private static final boolean USE_CACHE = false;
63
64    public static final int EVENT_DISPLAY_ADDED = 1;
65    public static final int EVENT_DISPLAY_CHANGED = 2;
66    public static final int EVENT_DISPLAY_REMOVED = 3;
67
68    private static DisplayManagerGlobal sInstance;
69
70    private final Object mLock = new Object();
71
72    private final IDisplayManager mDm;
73
74    private DisplayManagerCallback mCallback;
75    private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
76            new ArrayList<DisplayListenerDelegate>();
77
78    private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>();
79    private int[] mDisplayIdCache;
80
81    private int mWifiDisplayScanNestCount;
82
83    private DisplayManagerGlobal(IDisplayManager dm) {
84        mDm = dm;
85    }
86
87    /**
88     * Gets an instance of the display manager global singleton.
89     *
90     * @return The display manager instance, may be null early in system startup
91     * before the display manager has been fully initialized.
92     */
93    public static DisplayManagerGlobal getInstance() {
94        synchronized (DisplayManagerGlobal.class) {
95            if (sInstance == null) {
96                IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
97                if (b != null) {
98                    sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
99                }
100            }
101            return sInstance;
102        }
103    }
104
105    /**
106     * Get information about a particular logical display.
107     *
108     * @param displayId The logical display id.
109     * @return Information about the specified display, or null if it does not exist.
110     * This object belongs to an internal cache and should be treated as if it were immutable.
111     */
112    public DisplayInfo getDisplayInfo(int displayId) {
113        try {
114            synchronized (mLock) {
115                DisplayInfo info;
116                if (USE_CACHE) {
117                    info = mDisplayInfoCache.get(displayId);
118                    if (info != null) {
119                        return info;
120                    }
121                }
122
123                info = mDm.getDisplayInfo(displayId);
124                if (info == null) {
125                    return null;
126                }
127
128                if (USE_CACHE) {
129                    mDisplayInfoCache.put(displayId, info);
130                }
131                registerCallbackIfNeededLocked();
132
133                if (DEBUG) {
134                    Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
135                }
136                return info;
137            }
138        } catch (RemoteException ex) {
139            throw ex.rethrowFromSystemServer();
140        }
141    }
142
143    /**
144     * Gets all currently valid logical display ids.
145     *
146     * @return An array containing all display ids.
147     */
148    public int[] getDisplayIds() {
149        try {
150            synchronized (mLock) {
151                if (USE_CACHE) {
152                    if (mDisplayIdCache != null) {
153                        return mDisplayIdCache;
154                    }
155                }
156
157                int[] displayIds = mDm.getDisplayIds();
158                if (USE_CACHE) {
159                    mDisplayIdCache = displayIds;
160                }
161                registerCallbackIfNeededLocked();
162                return displayIds;
163            }
164        } catch (RemoteException ex) {
165            throw ex.rethrowFromSystemServer();
166        }
167    }
168
169    /**
170     * Gets information about a logical display.
171     *
172     * The display metrics may be adjusted to provide compatibility
173     * for legacy applications or limited screen areas.
174     *
175     * @param displayId The logical display id.
176     * @param daj The compatibility info and activityToken.
177     * @return The display object, or null if there is no display with the given id.
178     */
179    public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
180        DisplayInfo displayInfo = getDisplayInfo(displayId);
181        if (displayInfo == null) {
182            return null;
183        }
184        return new Display(this, displayId, displayInfo, daj);
185    }
186
187    /**
188     * Gets information about a logical display.
189     *
190     * The display metrics may be adjusted to provide compatibility
191     * for legacy applications or limited screen areas.
192     *
193     * @param displayId The logical display id.
194     * @param resources Resources providing compatibility info.
195     * @return The display object, or null if there is no display with the given id.
196     */
197    public Display getCompatibleDisplay(int displayId, Resources resources) {
198        DisplayInfo displayInfo = getDisplayInfo(displayId);
199        if (displayInfo == null) {
200            return null;
201        }
202        return new Display(this, displayId, displayInfo, resources);
203    }
204
205    /**
206     * Gets information about a logical display without applying any compatibility metrics.
207     *
208     * @param displayId The logical display id.
209     * @return The display object, or null if there is no display with the given id.
210     */
211    public Display getRealDisplay(int displayId) {
212        return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
213    }
214
215    public void registerDisplayListener(DisplayListener listener, Handler handler) {
216        if (listener == null) {
217            throw new IllegalArgumentException("listener must not be null");
218        }
219
220        synchronized (mLock) {
221            int index = findDisplayListenerLocked(listener);
222            if (index < 0) {
223                mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
224                registerCallbackIfNeededLocked();
225            }
226        }
227    }
228
229    public void unregisterDisplayListener(DisplayListener listener) {
230        if (listener == null) {
231            throw new IllegalArgumentException("listener must not be null");
232        }
233
234        synchronized (mLock) {
235            int index = findDisplayListenerLocked(listener);
236            if (index >= 0) {
237                DisplayListenerDelegate d = mDisplayListeners.get(index);
238                d.clearEvents();
239                mDisplayListeners.remove(index);
240            }
241        }
242    }
243
244    private int findDisplayListenerLocked(DisplayListener listener) {
245        final int numListeners = mDisplayListeners.size();
246        for (int i = 0; i < numListeners; i++) {
247            if (mDisplayListeners.get(i).mListener == listener) {
248                return i;
249            }
250        }
251        return -1;
252    }
253
254    private void registerCallbackIfNeededLocked() {
255        if (mCallback == null) {
256            mCallback = new DisplayManagerCallback();
257            try {
258                mDm.registerCallback(mCallback);
259            } catch (RemoteException ex) {
260                throw ex.rethrowFromSystemServer();
261            }
262        }
263    }
264
265    private void handleDisplayEvent(int displayId, int event) {
266        synchronized (mLock) {
267            if (USE_CACHE) {
268                mDisplayInfoCache.remove(displayId);
269
270                if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
271                    mDisplayIdCache = null;
272                }
273            }
274
275            final int numListeners = mDisplayListeners.size();
276            for (int i = 0; i < numListeners; i++) {
277                mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
278            }
279        }
280    }
281
282    public void startWifiDisplayScan() {
283        synchronized (mLock) {
284            if (mWifiDisplayScanNestCount++ == 0) {
285                registerCallbackIfNeededLocked();
286                try {
287                    mDm.startWifiDisplayScan();
288                } catch (RemoteException ex) {
289                    throw ex.rethrowFromSystemServer();
290                }
291            }
292        }
293    }
294
295    public void stopWifiDisplayScan() {
296        synchronized (mLock) {
297            if (--mWifiDisplayScanNestCount == 0) {
298                try {
299                    mDm.stopWifiDisplayScan();
300                } catch (RemoteException ex) {
301                    throw ex.rethrowFromSystemServer();
302                }
303            } else if (mWifiDisplayScanNestCount < 0) {
304                Log.wtf(TAG, "Wifi display scan nest count became negative: "
305                        + mWifiDisplayScanNestCount);
306                mWifiDisplayScanNestCount = 0;
307            }
308        }
309    }
310
311    public void connectWifiDisplay(String deviceAddress) {
312        if (deviceAddress == null) {
313            throw new IllegalArgumentException("deviceAddress must not be null");
314        }
315
316        try {
317            mDm.connectWifiDisplay(deviceAddress);
318        } catch (RemoteException ex) {
319            throw ex.rethrowFromSystemServer();
320        }
321    }
322
323    public void pauseWifiDisplay() {
324        try {
325            mDm.pauseWifiDisplay();
326        } catch (RemoteException ex) {
327            throw ex.rethrowFromSystemServer();
328        }
329    }
330
331    public void resumeWifiDisplay() {
332        try {
333            mDm.resumeWifiDisplay();
334        } catch (RemoteException ex) {
335            throw ex.rethrowFromSystemServer();
336        }
337    }
338
339    public void disconnectWifiDisplay() {
340        try {
341            mDm.disconnectWifiDisplay();
342        } catch (RemoteException ex) {
343            throw ex.rethrowFromSystemServer();
344        }
345    }
346
347    public void renameWifiDisplay(String deviceAddress, String alias) {
348        if (deviceAddress == null) {
349            throw new IllegalArgumentException("deviceAddress must not be null");
350        }
351
352        try {
353            mDm.renameWifiDisplay(deviceAddress, alias);
354        } catch (RemoteException ex) {
355            throw ex.rethrowFromSystemServer();
356        }
357    }
358
359    public void forgetWifiDisplay(String deviceAddress) {
360        if (deviceAddress == null) {
361            throw new IllegalArgumentException("deviceAddress must not be null");
362        }
363
364        try {
365            mDm.forgetWifiDisplay(deviceAddress);
366        } catch (RemoteException ex) {
367            throw ex.rethrowFromSystemServer();
368        }
369    }
370
371    public WifiDisplayStatus getWifiDisplayStatus() {
372        try {
373            return mDm.getWifiDisplayStatus();
374        } catch (RemoteException ex) {
375            throw ex.rethrowFromSystemServer();
376        }
377    }
378
379    public void requestColorMode(int displayId, int colorMode) {
380        try {
381            mDm.requestColorMode(displayId, colorMode);
382        } catch (RemoteException ex) {
383            throw ex.rethrowFromSystemServer();
384        }
385    }
386
387    /**
388     * Set the level of color saturation to apply to the display.
389     */
390    public void setSaturationLevel(float level) {
391        try {
392            mDm.setSaturationLevel(level);
393        } catch (RemoteException ex) {
394            throw ex.rethrowFromSystemServer();
395        }
396    }
397
398    public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
399            String name, int width, int height, int densityDpi, Surface surface, int flags,
400            VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
401        if (TextUtils.isEmpty(name)) {
402            throw new IllegalArgumentException("name must be non-null and non-empty");
403        }
404        if (width <= 0 || height <= 0 || densityDpi <= 0) {
405            throw new IllegalArgumentException("width, height, and densityDpi must be "
406                    + "greater than 0");
407        }
408
409        VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
410        IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
411        int displayId;
412        try {
413            displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken,
414                    context.getPackageName(), name, width, height, densityDpi, surface, flags,
415                    uniqueId);
416        } catch (RemoteException ex) {
417            throw ex.rethrowFromSystemServer();
418        }
419        if (displayId < 0) {
420            Log.e(TAG, "Could not create virtual display: " + name);
421            return null;
422        }
423        Display display = getRealDisplay(displayId);
424        if (display == null) {
425            Log.wtf(TAG, "Could not obtain display info for newly created "
426                    + "virtual display: " + name);
427            try {
428                mDm.releaseVirtualDisplay(callbackWrapper);
429            } catch (RemoteException ex) {
430                throw ex.rethrowFromSystemServer();
431            }
432            return null;
433        }
434        return new VirtualDisplay(this, display, callbackWrapper, surface);
435    }
436
437    public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
438        try {
439            mDm.setVirtualDisplaySurface(token, surface);
440        } catch (RemoteException ex) {
441            throw ex.rethrowFromSystemServer();
442        }
443    }
444
445    public void resizeVirtualDisplay(IVirtualDisplayCallback token,
446            int width, int height, int densityDpi) {
447        try {
448            mDm.resizeVirtualDisplay(token, width, height, densityDpi);
449        } catch (RemoteException ex) {
450            throw ex.rethrowFromSystemServer();
451        }
452    }
453
454    public void releaseVirtualDisplay(IVirtualDisplayCallback token) {
455        try {
456            mDm.releaseVirtualDisplay(token);
457        } catch (RemoteException ex) {
458            throw ex.rethrowFromSystemServer();
459        }
460    }
461
462    /**
463     * Gets the stable device display size, in pixels.
464     */
465    public Point getStableDisplaySize() {
466        try {
467            return mDm.getStableDisplaySize();
468        } catch (RemoteException ex) {
469            throw ex.rethrowFromSystemServer();
470        }
471    }
472
473    /**
474     * Retrieves brightness change events.
475     */
476    public List<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
477        try {
478            ParceledListSlice<BrightnessChangeEvent> events =
479                    mDm.getBrightnessEvents(callingPackage);
480            if (events == null) {
481                return Collections.emptyList();
482            }
483            return events.getList();
484        } catch (RemoteException ex) {
485            throw ex.rethrowFromSystemServer();
486        }
487    }
488
489    /**
490     * Sets the global brightness configuration for a given user.
491     *
492     * @hide
493     */
494    public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId,
495            String packageName) {
496        try {
497            mDm.setBrightnessConfigurationForUser(c, userId, packageName);
498        } catch (RemoteException ex) {
499            throw ex.rethrowFromSystemServer();
500        }
501    }
502
503    /**
504     * Gets the global brightness configuration for a given user or null if one hasn't been set.
505     *
506     * @hide
507     */
508    public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
509        try {
510            return mDm.getBrightnessConfigurationForUser(userId);
511        } catch (RemoteException ex) {
512            throw ex.rethrowFromSystemServer();
513        }
514    }
515
516    /**
517     * Gets the default brightness configuration or null if one hasn't been configured.
518     *
519     * @hide
520     */
521    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
522        try {
523            return mDm.getDefaultBrightnessConfiguration();
524        } catch (RemoteException ex) {
525            throw ex.rethrowFromSystemServer();
526        }
527    }
528
529    /**
530     * Temporarily sets the brightness of the display.
531     * <p>
532     * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
533     * </p>
534     *
535     * @param brightness The brightness value from 0 to 255.
536     *
537     * @hide Requires signature permission.
538     */
539    public void setTemporaryBrightness(int brightness) {
540        try {
541            mDm.setTemporaryBrightness(brightness);
542        } catch (RemoteException ex) {
543            throw ex.rethrowFromSystemServer();
544        }
545    }
546
547    /**
548     * Temporarily sets the auto brightness adjustment factor.
549     * <p>
550     * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
551     * </p>
552     *
553     * @param adjustment The adjustment factor from -1.0 to 1.0.
554     *
555     * @hide Requires signature permission.
556     */
557    public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
558        try {
559            mDm.setTemporaryAutoBrightnessAdjustment(adjustment);
560        } catch (RemoteException ex) {
561            throw ex.rethrowFromSystemServer();
562        }
563    }
564
565    /**
566     * Retrieves ambient brightness stats.
567     */
568    public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
569        try {
570            ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
571            if (stats == null) {
572                return Collections.emptyList();
573            }
574            return stats.getList();
575        } catch (RemoteException ex) {
576            throw ex.rethrowFromSystemServer();
577        }
578    }
579
580    private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
581        @Override
582        public void onDisplayEvent(int displayId, int event) {
583            if (DEBUG) {
584                Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
585            }
586            handleDisplayEvent(displayId, event);
587        }
588    }
589
590    private static final class DisplayListenerDelegate extends Handler {
591        public final DisplayListener mListener;
592
593        public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
594            super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
595            mListener = listener;
596        }
597
598        public void sendDisplayEvent(int displayId, int event) {
599            Message msg = obtainMessage(event, displayId, 0);
600            sendMessage(msg);
601        }
602
603        public void clearEvents() {
604            removeCallbacksAndMessages(null);
605        }
606
607        @Override
608        public void handleMessage(Message msg) {
609            switch (msg.what) {
610                case EVENT_DISPLAY_ADDED:
611                    mListener.onDisplayAdded(msg.arg1);
612                    break;
613                case EVENT_DISPLAY_CHANGED:
614                    mListener.onDisplayChanged(msg.arg1);
615                    break;
616                case EVENT_DISPLAY_REMOVED:
617                    mListener.onDisplayRemoved(msg.arg1);
618                    break;
619            }
620        }
621    }
622
623    private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
624        private VirtualDisplayCallbackDelegate mDelegate;
625
626        public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) {
627            if (callback != null) {
628                mDelegate = new VirtualDisplayCallbackDelegate(callback, handler);
629            }
630        }
631
632        @Override // Binder call
633        public void onPaused() {
634            if (mDelegate != null) {
635                mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED);
636            }
637        }
638
639        @Override // Binder call
640        public void onResumed() {
641            if (mDelegate != null) {
642                mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED);
643            }
644        }
645
646        @Override // Binder call
647        public void onStopped() {
648            if (mDelegate != null) {
649                mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED);
650            }
651        }
652    }
653
654    private final static class VirtualDisplayCallbackDelegate extends Handler {
655        public static final int MSG_DISPLAY_PAUSED = 0;
656        public static final int MSG_DISPLAY_RESUMED = 1;
657        public static final int MSG_DISPLAY_STOPPED = 2;
658
659        private final VirtualDisplay.Callback mCallback;
660
661        public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback,
662                Handler handler) {
663            super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
664            mCallback = callback;
665        }
666
667        @Override
668        public void handleMessage(Message msg) {
669            switch (msg.what) {
670                case MSG_DISPLAY_PAUSED:
671                    mCallback.onPaused();
672                    break;
673                case MSG_DISPLAY_RESUMED:
674                    mCallback.onResumed();
675                    break;
676                case MSG_DISPLAY_STOPPED:
677                    mCallback.onStopped();
678                    break;
679            }
680        }
681    }
682}
683